package cc.telecomdigital.tdstock.Framework;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import com.lightstreamer.ls_client.UpdateInfo;

import cc.telecomdigital.tdstock.R;
import cc.telecomdigital.tdstock.Framework.UniqueItem.CDRStorageAccess;
import cc.telecomdigital.tdstock.Framework.UniqueItem.CDRStorageAccess.CDRSingleStorageObject;
import cc.telecomdigital.tdstock.Framework.UniqueItem.CDRStorageAccess.IMMEDIATE_SEQ;
import cc.telecomdigital.tdstock.Services.TDStockReportCDRUsage;
import cc.telecomdigital.tdstock.Services.TDStockReportCDRUsage.TDStockReportCDRUsage_Result;
import cc.telecomdigital.tdstock.Framework.UniqueItem.DeviceInfo;
import cc.telecomdigital.tdstock.Framework.UniqueItem.DisplayChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.TDStockLog;
import cc.telecomdigital.tdstock.Framework.UniqueItem.DisplayChangeListener.IDisplayOnOffChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.NetworkChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.NetworkChangeListener.CONNECT_TYPE;
import cc.telecomdigital.tdstock.Framework.UniqueItem.NetworkChangeListener.INetworkChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.PhoneIncomingCallListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.PhoneIncomingCallListener.IPhoneIncomingCallListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.PhoneIncomingCallListener.PHONE_STATE;
import cc.telecomdigital.tdstock.Framework.UniqueItem.ScreenOrientationChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.ScreenOrientationChangeListener.IScreenOrientationChangeListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.ScreenOrientationChangeListener.SCR_ORIENTATION;
import cc.telecomdigital.tdstock.Framework.UniqueItem.UnLockScreenListener;
import cc.telecomdigital.tdstock.Framework.UniqueItem.UnLockScreenListener.IUnLockScreenListener;
import cc.telecomdigital.tdstock.Services.LS_ClientParamMemory;
import cc.telecomdigital.tdstock.Services.LS_ClientParamMemory.INFO_MODE;
import cc.telecomdigital.tdstock.Services.LSStringList;
import cc.telecomdigital.tdstock.Services.LS_SubscriptionItem;
import cc.telecomdigital.tdstock.Services.LS_UserClient;
import cc.telecomdigital.tdstock.Services.LS_UserClient.CONNECTION_STATUS;
import cc.telecomdigital.tdstock.Services.LS_UserClient.ILS_UserClient_Authentication_Report;
import cc.telecomdigital.tdstock.Services.LS_UserClient.ILS_UserClient_ConnectionListener;
import cc.telecomdigital.tdstock.Services.LS_UserClient.ILS_UserClient_Response;
import cc.telecomdigital.tdstock.Services.LS_UserClient.LS_UserClient_Authentication_Result;
import cc.telecomdigital.tdstock.Services.TDStockReqFreeTrial;
import cc.telecomdigital.tdstock.Services.TDStockReportCDRUsage.ITDStockReportCDRUsage_Result;
import cc.telecomdigital.tdstock.Services.TDStockReqFreeTrial.ITDStockReqFreeTrial_Result;
import cc.telecomdigital.tdstock.Services.TDStockReqFreeVerify;
import cc.telecomdigital.tdstock.Services.TDStockReqFreeVerify.ITDStockReqFreeVerify_Result;
import cc.telecomdigital.tdstock.Tools.ActivityController;
import cc.telecomdigital.tdstock.Tools.CDRRecordItem;
import cc.telecomdigital.tdstock.Tools.LS_SubscriptionPair;
import cc.telecomdigital.tdstock.Tools.ReporterTicker;
import cc.telecomdigital.tdstock.Tools.ReporterTicker.IReporterTicker_Report;
import android.app.Activity;
import android.app.KeyguardManager;

import android.app.Application;
import android.content.Context;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Message;

public abstract class Application_Framework extends Application implements
	INetworkChangeListener, IDisplayOnOffChangeListener,
	IScreenOrientationChangeListener, ILS_UserClient_Authentication_Report,
	IUnLockScreenListener,	
	ILS_UserClient_Response, IPhoneIncomingCallListener
{
	public enum LS_ItemRequestType { NONE, NORMAL_CHARGE, BOOKMARK_CHARGE }
	
	public interface IActivityChangeListener
	{
		/** Report the new activity that is being focus **/
		void IActivityChange_Response(Class<? extends Activity> iOnClass);
	}

	private boolean enforceCDRTesting = false;
	
	private final boolean         cdrDebug  = false;
	private final boolean		debugMode	= false;
	private boolean			isActive;
	//To set this to true, CDR consumption should increase, because connection could be made when the
	//lock screen is on
	private  boolean         	unlockScreenConnectOption = false;
	
	// Controller for the main activity list
	public ActivityController	actListController;

	/** Get Context instance of the application, need for dialog and some others **/
	public Context GetApplicationInstance()
	{
		return this;		
	}

	/** sharable internal synLock **/	
	private Object								padLock = new Object();
	
	/** Prevent Network list mutli-access **/
	private Object 							networkListLock  = new Object();
	/** Network Change Broadcast register item **/
	private NetworkChangeListener					networkListener;
	/** Network Change Listener List **/
	private List<INetworkChangeListener>			networkListenerList;

	/** Prevent Display OnOff list multi-access **/
	private Object 							displayOnOffListLock = new Object();
	/** Display on/off Broadcast register item **/
	private DisplayChangeListener					displayOnOffListener;
	/** Display on off Listener List **/
	private List<IDisplayOnOffChangeListener>		displayOnOffListenerList;

	/** Prevent Display Unlock list multi-access **/	
//	private Object 							unlockListLock = new Object();
	private UnLockScreenListener					unLockScreenListener;	
//	private List<IUnLockScreenListener>			unLockScreenListenerList;

	/** Prevent Screen Change access list multi-access **/	
	private Object 							scrOrientationListLock = new Object();
	/** Screen Orientation Broadcast register item **/
	private ScreenOrientationChangeListener			scrOrientationListener;
	/** Screen Orientation Listener List **/	
	private List<IScreenOrientationChangeListener>	scrOrientationListenerList;
	
	/** Prevent activity Change access list multi-access **/	
	private Object 							actChangeListLock = new Object();
	/** Looking for activity Change **/
	private List<IActivityChangeListener>			actChangeListenerList;
	
	private PhoneIncomingCallListener                 phoneIncomingCallListener;
	//by zor
	public boolean                                   screenIsLocked;
	
	// Make Application as singleton for easier access
	private static Application_Framework			appInstance = null;

	private static CDRAutoReportQueue                 cdrItemReporter;

	private KeyguardManager 						keyGuardManager;
	/** Get Single Instance of the Application **/
	public static Application_Framework GetInstance()
	{
		return appInstance;
	}

	private String	className	= null;

	protected String GetIdentity()
	{
		if (className == null)
			className = getClass().getName();
		return className;
	}

	private static int runningIndex = 0;
	/** Get an unique Index to be used, each getting brings an increment of 1 **/
	public static int GetRunningIndex()
	{
		int index;
		
		index = runningIndex;
		++runningIndex;
		
		return index;
	}
		
	public Application_Framework()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application_Framework Constructor called");
		
		isActive = true;
				
		actListController   = new ActivityController(this, null);

		// List for Network change report
		networkListenerList = new ArrayList<INetworkChangeListener>();
		// List for listening the Display
		displayOnOffListenerList      = new ArrayList<IDisplayOnOffChangeListener>();
		// List for Screen orientation
		scrOrientationListenerList    = new ArrayList<IScreenOrientationChangeListener>();
		actChangeListenerList         = new ArrayList<IActivityChangeListener>();
//		unLockScreenListenerList      = new ArrayList<IUnLockScreenListener>();
		
		// Network Change Listener
		networkListener        = new NetworkChangeListener(this, this);		
		screenIsLocked         = false;
		
		curClass = null;
		// Initialize the charging parameter
		InitialInternalSubscribeItemsFlag();		
	}
		
	// *** Activity Focus Change Listener **/
	// Yes we cheat here, since there is no way to get the class name.
	// Each Activity from the framework requires to perform this when they
	// are on Resume
	private Activity	curClass;

	/** Update by the child to report what activity the application is currently at
	    not the GroupHost Activity is ignored **/
	public void SetAsActiveActivity(Activity iActivityClass)
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "SetAsActiveActivity: " + iActivityClass.getClass().getName());

		if ((curClass == null) || (curClass.equals(iActivityClass) == false))
			SetFocusActClass(iActivityClass);
	}

	/** Private Reporter for class focus Change **/
	private void SetFocusActClass(Activity iActivityClass)
	{
		synchronized(actChangeListLock)
		{
			curClass = iActivityClass;
			
			if (actChangeListenerList.size() == 0)
				return;
			
			Message msg;
			
			msg = new Message();
			msg.what = UPDATE_ACTIVITYCHANGE_CALLER;
			
			for (IActivityChangeListener item: actChangeListenerList)
			{
				msg.obj = item;
				localMsgHandler.sendMessage(msg);
			}
		}
	}			
	
	/** Activity Register for checking Activity Change, design mostly for Other type
	   of activity, such as TabActivity, ListActivity and etc.. Using RegisterToListenActivityChange 
	   must also use UnregisterToListenActivityChange on exit, works as pair **/
	public boolean RegisterToListenActivityChange(IActivityChangeListener iRegItem)
	{
		if (iRegItem == null)
			return false;
			
		synchronized(actChangeListLock)
		{			
			if (actChangeListenerList.contains(iRegItem) == true)
				return true;
			
			actChangeListenerList.add(iRegItem);
		}
		return true;
	}
	
	/** Activity UnRegister for checking Activity Change, design mostly for Other type
	   of activity, such as TabActivity, ListActivity and etc.. Using UnregisterToListenActivityChange 
	   must also use RegisterToListenActivityChange on exit, works as pair **/	
	public boolean UnregisterToListenActivityChange(IActivityChangeListener iRegItem)
	{		
		if (iRegItem == null)
			return false;
		
		synchronized(actChangeListLock)
		{
			int index;
			
			index = actChangeListenerList.indexOf(iRegItem);
			if(index != -1)			
				actChangeListenerList.remove(index);
		}
		return true;		
	}		
		
	public Activity GetCurrentActivity()
	{
		return curClass;
	}
	
	/** Obtain the Current Focus Class **/
	public Class<? extends Activity> GetCurrentActivityClass()
	{
		return curClass == null ? null : curClass.getClass();
	}

	// *** Listener Defined ends here ***
	
	
	@Override
	public void onCreate()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application_Framework onCreate called");
		
		super.onCreate();
		appInstance     = this;
		LSClient_Create(this);
		// Display Change Listener
		displayOnOffListener = new DisplayChangeListener(this, this);
		scrOrientationListener = new ScreenOrientationChangeListener(this, this);
		unLockScreenListener   = new UnLockScreenListener(this, this);
		
		if (NOT_APP_EXIT_OPTION == false)
			phoneIncomingCallListener = new PhoneIncomingCallListener(this, this);
		
		networkListener.Enabled();
		// Always interest on display on-off
		displayOnOffListener.Enable();
		cdrItemReporter = new CDRAutoReportQueue(this);
		cdrItemReporter.SetDebugMode(cdrDebug);

		keyGuardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);		
	}

	//** Set Default orientation, which would directly affect the overall rotation reply
	public void SetInitOrientation(SCR_ORIENTATION iDefaultOrientation)
	{
		if (iDefaultOrientation == SCR_ORIENTATION.PORTRAIT)
			scrOrientationListener.SetTabletVersion(false);
		else
			scrOrientationListener.SetTabletVersion(true);			
	}

	//Use only once for Termination notification
	private boolean onTermianting = false;	
	// Guess this is never call, control by the system
	@Override
	public void onTerminate()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onTerminate Called");
		
		onTermianting = true;
		TerminateServicesAndObject();
		super.onTerminate();
		//System.runFinalizersOnExit(true);	  //modified by haib in 2012-03-29, for we need  to restart the app.
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onTerminate Called End");		
		//System.exit(0);		  //modified by haib in 2012-03-29, for we need  to restart the app.
	}

/** Andy **/	
	/** Suspend all servied and disconnect from light streamer, some service are remain active **/
	public void onSuspend()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(),"Suspend Call");
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onSuspend: isActive=" + isActive);	
		
		if (isActive == false)
			return;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(),"Suspend Start");

		isActive = false;
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onSuspend: Set isActive=" + isActive);

		ClearFrameworkMessage();

//		networkListener.Disabled();
		scrOrientationListener.Disable();		
										
		String reportString;
		//Before that we would try to end our CDRSession
		reportString = FinalizeChargingRecord();
		//Delete our record
		chargingRecord = null;		
		if (chargingTimer != null)
		{
			chargingTimer.Dispose();
			chargingTimer = null;
		}
		
		if (reportString.equals("") == false)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "Suspend: Have a immediate Record");
						
			if (cdrItemReporter.AddAndSendImmediately(reportString) == true)
			{			
				//Give some time for it to send immediately
				if (debugMode)
					TDStockLog.i(GetIdentity(), "Suspend: Setting Delay for CDR report");
				try
				{
					Thread.sleep(500);
				}
				catch (InterruptedException ex)
				{
					// TODO Auto-generated catch block
					ex.printStackTrace();
					TDStockLog.e(GetIdentity(), "Suspend: " + ex.toString());
				}
			}				
		}
		
		if (isLSConnecting)
		{
			Dismiss();
			return;
		}
		
		//Reset both flag to fix in-complete connection
		isLSConnecting = false;
		isLSDisconnecting = false;
		
		//Cheap way to ask light streamer to release susbscription
		lsClient.ClearAllSubscribeItems();		
		//Terminate the connection		
		LSClient_Disconnect(CONNECTION_STATUS.CLOSE_ON_REST);
		
		if (cdrItemReporter != null)
			cdrItemReporter.PauseProcess();

		if (debugMode)
			TDStockLog.i(GetIdentity(),"Suspend Done");	
	}
	
	/** Application immediate dismiss function, process will be kill and all objects
	    will be clean up.  Considered as a safe exit function
	    Enforce a killing of self **/
	public void Dismiss()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application Dismiss called");
			
		onTermianting = true;		
		TerminateServicesAndObject();

		// Our design does not allow activity to state, so setting the curact to finish help
		// to clear the application memory
		Activity  curAct;		
		
		curAct = GetCurrentActivity();
		if (curAct != null)
		{
			while(curAct.getParent() != null)
				curAct = curAct.getParent();
		}			
		
		if (curAct != null)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "Killing our last activity: " + curAct.getClass().getName());
			try
			{
				curAct.finish();
			}
			catch(Exception e)
			{
				TDStockLog.e(GetIdentity(), "Dismiss: " + e.toString());
			}
		}		
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application Says Bye Bye");
		//System.runFinalizersOnExit(true);		 //modified by haibo in 2012-03-29, for we need to restart the app.
		android.os.Process.killProcess(android.os.Process.myPid());
		//Also need to clear the message content		
		//System.exit(0);  //modified by haibo in 2012-03-29, for we need to restart the app.
	}

	private String FinalizeChargingRecord()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "FinalizeChargingRecord Enter. ");
				
		//Check if it is a valid record		
		if (chargingRecord == null)
		{
			if (cdrDebug)
				TDStockLog.i(GetIdentity(), "FinalizeChargingRecord because Empty Info");			
			return "";
		}
		
		if (chargingRecord.GetStartRunningTime() == null)		
		{
			if (cdrDebug)
				TDStockLog.i(GetIdentity(), "FinalizeChargingRecord because Empty Running Time");			
			return "";
		}		
				
		if (chargingRecord.GetLastAccessTime() == null)
			chargingRecord.SetLastAccessTime();
		
		chargingRecord.SetEndRunningTime();

		String newURLString;
		
		if (usingFreeTrialCode)
			newURLString = LS_ClientParamMemory.Create_CDRFreeTrialReportURL(this, chargingRecord, ONE_MINUTE);
		else
		{
			if (lastUserLogonName == null)
				newURLString = "";
			else
				newURLString = LS_ClientParamMemory.Create_CDRReportURL( this, lastUserLogonName, GetLSClient_AccountName(), 
														  chargingRecord, ONE_MINUTE);
		}
		
		if (cdrDebug)
			TDStockLog.i(GetIdentity(), "FinalizeChargingRecord Created. ");		
		
		return newURLString;
	}
	
	/** Internal Object Cleanup function **/
	private void TerminateServicesAndObject()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(),"TerminateServicesAndObject Start");

		ClearFrameworkMessage();		
		
		actChangeListenerList.clear();
//		unLockScreenListenerList.clear();
		lsConnectStatusList.clear();
		displayOnOffListenerList.clear();	
		scrOrientationListenerList.clear();
		networkListenerList.clear();

		LSClient_Disconnect(CONNECTION_STATUS.CLOSE_ON_REST);
		
		if (displayOnOffListener != null)
			displayOnOffListener.Dispose();
		
		if (unLockScreenListener != null)
			unLockScreenListener.Dispose();
		
		if (phoneIncomingCallListener != null)
			phoneIncomingCallListener.Dispose();

		if (scrOrientationListener != null)
			scrOrientationListener.Dispose();		
		
		String reportString;
		//Before that we would try to end our CDRSession
		reportString = FinalizeChargingRecord();
		//Delete our record
		chargingRecord = null;		
		if (chargingTimer != null)
		{
			chargingTimer.Dispose();
			chargingTimer = null;
		}
		
		if (reportString.equals("") == false)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "TerminateServicesAndObject: Have a immediate Record");

			if (cdrItemReporter.AddAndSendImmediately(reportString) == true)
			{			
				//Give some time for it to send immediately
				if (debugMode)
					TDStockLog.i(GetIdentity(), "TerminateServicesAndObject: Setting Delay for CDR report");
				try
				{
					Thread.sleep(500);
				}
				catch (InterruptedException ex)
				{
					// TODO Auto-generated catch block
					ex.printStackTrace();
					TDStockLog.e(GetIdentity(), "TerminateServicesAndObject: " + ex.toString());
				}
			}				
		}

		if (networkListener != null)
			networkListener.Dispose();

		if (lsClient != null)
			lsClient.Dispose();

		if (cdrItemReporter != null)
		{
			cdrItemReporter.Dispose();
			cdrItemReporter = null;
		}

		if (debugMode)
			TDStockLog.i(GetIdentity(),"TerminateServicesAndObject Done");		
	}

	/** Remove all queuing message if found **/	
	private void ClearFrameworkMessage()
	{
		localMsgHandler.removeMessages(UPDATE_DISPLAY_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_NETWORK_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_ORIENTATION_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_AUTHENTICATION_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_ACTIVITYCHANGE_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_LS_CONNECT_CALLER, null);
		localMsgHandler.removeMessages(UPDATE_LS_TIMER_CALLER, null);
//		localMsgHandler.removeMessages(UPDATE_UNLOCKSCREEN_CALLER, null);
	}

	/** Do not put a check case here, prepare for fail over
	    When the  pause, currently is when user press the Home Key
	    Put the application into sleep mode, while most of the listener will be stopped
	    and light streamer will be on disconnect **/
	public void onPauseAction()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onPauseAction: isActive=" + isActive);

		if (isActive == false)
			return;

		scrOrientationListener.Disable();
		LSClient_Disconnect(CONNECTION_STATUS.CLOSE_ON_REST);

		isActive = false;
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onPauseAction: Set isActive=" + isActive);	
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application is onPause Action");		
	}

	private final int	UPDATE_DISPLAY_CALLER		= 1;
	private final int	UPDATE_NETWORK_CALLER		= 2;
	private final int	UPDATE_ORIENTATION_CALLER	= 3;
	private final int	UPDATE_AUTHENTICATION_CALLER	= 4;
	private final int   UPDATE_ACTIVITYCHANGE_CALLER  = 5;   // Activity Change
	private final int   UPDATE_LS_CONNECT_CALLER      = 6;   // Update LightStreamer Connection Status
	private final int   UPDATE_LS_TIMER_CALLER        = 7;
//	private final int   UPDATE_UNLOCKSCREEN_CALLER    = 8;

	private Handler	localMsgHandler = new Handler()
	{
		// Need to use try and catch
		public void handleMessage( Message msg)
		{
			switch (msg.what)
			{
				// Report as need, system should be turn on
				case UPDATE_DISPLAY_CALLER:
					try
					{
						IDisplayOnOffChangeListener actClass;

						if (msg.obj == null)
							break;

						actClass = (IDisplayOnOffChangeListener)msg.obj;						
						actClass.DisplayOnOff_Response(displayOnOffListener.IsDisplayOn());
					}
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_DISPLAY_CALLER: " + ex.toString());
					}
					break;
				case UPDATE_NETWORK_CALLER:
					try
					{
						INetworkChangeListener actClass;

						if (msg.obj == null)
							break;

						actClass = (INetworkChangeListener) msg.obj;
						actClass.NetworkChange_Response(networkListener.GetConnectionType(),
												   networkListener.GetNetworkInfo());
					}	
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_NETWORK_CALLER: " + ex.toString());
					}					
					break;
				case UPDATE_ORIENTATION_CALLER:
					try
					{
						IScreenOrientationChangeListener actClass;
						
						if (msg.obj == null)
							break;
						
						actClass = (IScreenOrientationChangeListener) msg.obj;
						actClass.ScreenOrientation_Response(scrOrientationListener.GetScreenOrientation());
					}	
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_ORIENTATION_CALLER: " + ex.toString());
					}					
					break;
				case UPDATE_AUTHENTICATION_CALLER:
					try					
					{
						LS_UserClient_Authentication_Result result;

						if (msg.obj == null)
							break;

						result = (LS_UserClient_Authentication_Result)msg.obj;						
						
						//Flush the response after use
						if (authResponse != null)
						{					
							if (debugMode)
								TDStockLog.i(GetIdentity(), "Return of login result");
							authResponse.LS_UserClient_Authentication_Response(result);
							authResponse = null;																																
						}

/* Andy */						
//						//Post a fail reply to any one who is interested on the connections status
//						if (result == null || result.successfulSend == false)
//							LS_UserClient_ConnectionReport(CONNECTION_STATUS.CONNECTION_FAIL);
//						else if (result.statusCode.equals("0") == false)
//						{
//							if(result.statusCode.equals("4"))  /* Invalid Logon name */
//								LS_UserClient_ConnectionReport(CONNECTION_STATUS.CONNECTION_INV_USER);
//							else if (result.statusCode.equals("5")) /* Invalid Logon password */
//								LS_UserClient_ConnectionReport(CONNECTION_STATUS.CONNECTION_INV_PASSWD);
//							else
//								LS_UserClient_ConnectionReport(CONNECTION_STATUS.CONNECTION_REJECT);
//						}
//						else if (result.statusCode == null)
//							LS_UserClient_ConnectionReport(CONNECTION_STATUS.CONNECTION_REJECT);
					}
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_AUTHENTICATION_CALLER: " + ex.toString());
					}
					break;
				case UPDATE_ACTIVITYCHANGE_CALLER:
					try
					{
						IActivityChangeListener actClass;

						if (msg.obj == null)
							break;

						actClass = (IActivityChangeListener) msg.obj;
						actClass.IActivityChange_Response(GetCurrentActivityClass());						
					}
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_ACTIVITYCHANGE_CALLER: " + ex.toString());
					}						
					break;
				case UPDATE_LS_CONNECT_CALLER:
					try
					{
						ILS_UserClient_ConnectionListener actClass;

						if (msg.obj == null)
							break;

						actClass = (ILS_UserClient_ConnectionListener) msg.obj;
						actClass.LS_UserClient_ConnectionReport(lsClient.GetLastConnectStatus());
					}	
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_LS_CONNECT_CALLER: " + ex.toString());
					}						
					break;

				case UPDATE_LS_TIMER_CALLER:
					try
					{
						IReporterTicker_Report actClass;
						int                    second;

						if (msg.obj == null)
							break;

						actClass = (IReporterTicker_Report)msg.obj;
						second   =  msg.arg1;
						actClass.ReporterTicker_Response(second);							
					}
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "UPDATE_LS_TIMER_CALLER: " + ex.toString());
					}
					break;
//				case UPDATE_UNLOCKSCREEN_CALLER:
//					try
//					{
//						IUnLockScreenListener actClass;
//						
//						if (msg.obj == null)
//							break;
//						
//						actClass = (IUnLockScreenListener)msg.obj;
//						actClass.UnLockScreen_Response();						
//					}
//					catch(Exception ex)
//					{
//						TDStockLog.e(GetIdentity(), "UPDATE_UNLOCKSCREEN_CALLER: " + ex.toString());						
//					}
//					break;
			}
		}
	};

	/** Application on Pause Call feature.  Stop process if required.
	    Create a timer to check whether we have actiivty in list, if know we
	    know we are not in focus **/
	public void onPause()
	{
		// Just turn off the screen orientation if no-one us using it
		if (scrOrientationListenerList.size() == 0)
			scrOrientationListener.Disable();

	}

	//Set this to make the app suspend, no changes
	private boolean NOT_APP_EXIT_OPTION = true;

	public boolean AllowToTerminateApp()
	{
		return NOT_APP_EXIT_OPTION;
	}

	/** Application on Resume Call feature.  Resume all necessary feature when
	    the app or activity is being waked up
	    Do not put a check case here, prepare for fail over **/
	public void onResume()
	{
		/* If the screen is still locked, we can not be resume */
		if (screenIsLocked)
			return;

		/** Andy **/
		if (NOT_APP_EXIT_OPTION)
			networkListener.Enabled();

		if (cdrItemReporter != null)
			cdrItemReporter.ResumeProcess();		

		if (debugMode)
			TDStockLog.i(GetIdentity(), "onResume: isActive=" + isActive);

		if (isActive == true)
			return;

		isActive = true;
		if (debugMode)
			TDStockLog.i(GetIdentity(), "onResume: Set isActive=" + isActive);			

		if (scrOrientationListenerList.size() > 0)
			scrOrientationListener.Enable();

		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application is onResume");
	}

	
//	/** Register to Listen for User Enter Action, Slide to activate the phone
//	  * RegisterToListenUnlockScreen and UnregisterToListenUnlockScreen, works in pair
//	  * The report is in UI thread.	    
//	  * If it is the identical, it will be replaced, One registration is allowed
//	  * for one context.  An update is posted immediately if registration is valid
//	  * @param  iActivity - instance with implement of IUnLockScreenListener
//	  * @return true for successfully register, false for if it is already in the list
//	  */
//	public boolean RegisterToListenUnlockScreen( IUnLockScreenListener iActivity)
//	{
//		if (iActivity == null)
//			return false;
//		
//		synchronized(unlockListLock)
//		{		
//			if (unLockScreenListenerList.contains(iActivity))
//				return true;
//
//			return unLockScreenListenerList.add(iActivity);
//		}
//	}
//
//	/** Unregister to Listen for User Enter Action
//	  * RegisterToListenUnlockScreen and UnregisterToListenUnlockScreen, works in pair
//	  * @param  iActivity - instance with implement of IUnLockScreenListener
//	  * @return true for successfully unregister, false for if it is not in the list
//	  */	    
//	public boolean UnregisterToListenUnlockScreen(IUnLockScreenListener iActivity)
//	{		
//		if (iActivity == null)
//			return false;
//
//		synchronized(unlockListLock)
//		{	
//			int index;
//			
//			index = unLockScreenListenerList.indexOf(iActivity);
//			if (index != -1)
//				unLockScreenListenerList.remove(index);			
//		}
//		return true;
//	}
//	
	/** Screen Lock and unlock notification **/	
	@SuppressWarnings("rawtypes")
	public void UnLockScreen_Response()
	{
		/* Resume the application when user unlock the phone */
		if (debugMode)
			TDStockLog.i(GetIdentity(), "User has unlock the screen");
		
		screenIsLocked = false;

		if (NOT_APP_EXIT_OPTION)
		{			
			Abstract_Activity_Framework curActObj;
			
			curActObj = null;
			if (GetCurrentActivity() != null)
				curActObj = (Abstract_Activity_Framework)GetCurrentActivity();

			if ((curActObj != null) && (curActObj.isUIActive))		
				isActive = true;
			else
			{
				if (debugMode)
					TDStockLog.i(GetIdentity(), "UnLockScreen_Response; None of a avaialble UI for our App on display");
			}

			if (isActive == false)
				return;
		}			
		
		onResume();

		//Perform an internal subscription
//Andy		
//		InternalSubscribeItems();
//Zor		
		ReLogonToServer(null);
		LSClient_ResumeAllItems(GetCurrentActivityClass());
//Zor		
//		//Provide message for unlock screen update
//		Message newMessage;
//		
//		//Provide message for Ls Connection status		
//		newMessage = new Message();
//		newMessage.what = UPDATE_LS_CONNECT_CALLER;
//		synchronized(lsConnectStatusListLock)
//		{
//			for (ILS_UserClient_ConnectionListener item : lsConnectStatusList)
//			{
//				newMessage.obj = item;
//				localMsgHandler.sendMessage(newMessage);					
//			}
//		}
	}

	private PHONE_STATE phoneDetectState = PHONE_STATE.IDLE;
	public void PhoneIncomingCall_Response(PHONE_STATE iState)
	{
		/* Resume the application when user unlock the phone */
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Application Phone State: " + iState);
		phoneDetectState = iState;
	}
	
	public PHONE_STATE GetCurrentPhoneState()
	{
		return phoneDetectState;
	}
	
	
	// ***************** Start Designed Listeners API **************************		
	/** Register to Listen for Display on/off
	  * RegisterToListenDisplayChange and UnregisterToListenDisplayChange, works in pair
	  * The report is in UI thread.	    
	  * If it is the identical, it will be replaced, One registration is allowed
	  * for one context.  An update is posted immediately if registration is valid
	  * @param  iActivity - instance with implement of IDisplayOnOffChangeListener
	  * @return true for successfully register, false for if it is already in the list
	  */
	public boolean RegisterToListenDisplayChange( IDisplayOnOffChangeListener iActivity)
	{
		boolean status, wasEnable;

		if (iActivity == null)
			return false;

		synchronized(displayOnOffListLock)
		{
			if (displayOnOffListenerList.contains(iActivity))
				return true;

			wasEnable = displayOnOffListener.IsEnabled();
			status = displayOnOffListenerList.add(iActivity);
		}
		
		if (wasEnable)
		{
			Message newMessage;

			newMessage = new Message();
			newMessage.what = UPDATE_DISPLAY_CALLER;
			newMessage.obj = iActivity;
			localMsgHandler.sendMessage(newMessage);
		}
		else
			displayOnOffListener.Enable();

		return status;
	}

	/** Unregister to Listen for Display on/off
	  * RegisterToListenDisplayChange and UnregisterToListenDisplayChange, works in pair
	  * @param  iActivity - instance with implement of IDisplayOnOffChangeListener
	  * @return true for successfully unregister, false for if it is not in the list
	  */	    
	public boolean UnregisterToListenDisplayChange(IDisplayOnOffChangeListener iActivity)
	{		
		if (iActivity == null)
			return false;

		synchronized(displayOnOffListLock)
		{
			int index;

			index = displayOnOffListenerList.indexOf(iActivity);
			if (index != -1)
				displayOnOffListenerList.remove(index);	
		}			
		return true;
	}

	/** Provide information about the current display is on/off **/
	public boolean IsDisplayOn()
	{
		return displayOnOffListener.IsDisplayOn();
	}

	/** Report a change of the event, It will use send message to enter the UI thread
	  * It is up to us to decide whether to use Thread or not. If we using Thread, the
	  * receiver might need extra coding to perform the UI stuff
	  * ** Means to be used internally by the application only
	  */
	@Override
	public void DisplayOnOff_Response(boolean iOnOff)
	{
		/* We should check whether the screen is locked */
		if (iOnOff)
		{
			boolean isShown;
			if (keyGuardManager != null)
				isShown = keyGuardManager.inKeyguardRestrictedInputMode();
			else
				isShown = false;
			
			if (isShown == false)
			{
				if (debugMode)
					TDStockLog.i(GetIdentity(), "Screen is on, and have no locked");
				this.UnLockScreen_Response();
			}
		}
		
		/* Pause the system when the display is off */
		if (iOnOff == false)
		{
			screenIsLocked = true;
			if (NOT_APP_EXIT_OPTION)
				onSuspend();
			else
				onPauseAction();
		}
		
		synchronized (displayOnOffListLock)
		{
			Message newMessage;

			newMessage = new Message();
			newMessage.what = UPDATE_DISPLAY_CALLER;			
			for (IDisplayOnOffChangeListener item : displayOnOffListenerList)
			{		
				newMessage.obj = item;
				localMsgHandler.sendMessage(newMessage);
			}
		}
	}

	// ********** Networking related **********
	// If it is the identical, it will be replaced, One registration is allowed
	// for one context
	/** Register to Listen for Network Change, WIFI, NONE, MOBILE_DATA
	  * RegisterToListenNetworkChange and UnregisterToListenNetworkChange, works in pair
	  * The report is in UI thread.	    
	  * If it is the identical, it will be replaced, One registration is allowed
	  * for one context
	  * @param  iActivity - instance with implement of INetworkChangeListener
	  * @return true for successfully register, false for if it is already in the list
	  * */ 	
	public boolean RegisterToListenNetworkChange(INetworkChangeListener iActivity)
	{
		if (iActivity == null)
			return false;

		boolean status, wasEnable;
		
		synchronized(networkListLock)
		{		
			if (networkListenerList.contains(iActivity))
				return true;
	
			wasEnable = networkListener.IsEnabled();	
			status = networkListenerList.add(iActivity);
		}
		
		if (wasEnable)
		{
			Message newMessage;

			newMessage = new Message();
			newMessage.what = UPDATE_NETWORK_CALLER;
			newMessage.obj = iActivity;
			localMsgHandler.sendMessage(newMessage);
		}
		else
			networkListener.Enabled();

		return status;
	}

	/** Unregister to Listen for Network Change, WIFI, NONE, MOBILE_DATA
	  * RegisterToListenNetworkChange and UnregisterToListenNetworkChange, works in pair
	  * @param  iActivity - instance with implement of INetworkChangeListener
	  * @return true for successfully unregister, false for if it is already in the list
	  */ 	
	public boolean UnregisterToListenNetworkChange(
			INetworkChangeListener iActivity)
	{		
		if (iActivity == null)
			return false;

		synchronized (networkListLock)
		{
			int index;

			index = networkListenerList.indexOf(iActivity);
			if (index != -1)
				networkListenerList.remove(index);	
		}			
		return true;
	}
	
	/** Get Current Connection Type, MOBILE, WIFI, NONE 
	 *  @return CONNECT_TYPE enum
	 */
	public CONNECT_TYPE GetConnectionType()
	{
		return networkListener.GetConnectionType();
	}	

	/** Easy check to known whether there are network connection path 
	 *  @return boolean for connection available 
	 */
	public boolean IsNetworkAvailable()
	{
		if (networkListener.GetConnectionType() == CONNECT_TYPE.NONE)
			return false;

		return true;
	}
	
	/** Get Current connected network information, could be null if
	  * no network is attached
	  * @return NetworkInfo - current connection info 
	  */
	public NetworkInfo GetAvailableNetworkInfo()
	{
		return networkListener.GetNetworkInfo();
	}
	
	private CONNECT_TYPE prevConnectType = CONNECT_TYPE.UNKNOWN;
	/** report a change of the event, It will use send message to enter the UI thread
	  * It is up to us to decide whether to use Thread or not. If we using Thread, the
	  * receiver might need extra coding to perform the UI stuff 
	  * ** Means to be used internally by the application only
	  */
	@Override
	public void NetworkChange_Response( CONNECT_TYPE iConnectType,
								 NetworkInfo iNetInfo)
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "NetworkChange_Response: isActive=" + isActive 
								   + ", ConnectionType=" + iConnectType.toString());

		//If we are on switching network we would fool the device that we are being dropped 
		if ((prevConnectType != CONNECT_TYPE.UNKNOWN) && (prevConnectType != iConnectType))
		{
			if (lsClient.IsConnected())
			{
				lsClient.ClearAllSubscribeItems();					
				try
				{				
					Thread.sleep(disconnectDelayWait);				
				}
				catch(Exception ex)
				{
					TDStockLog.e(GetIdentity(), "NetworkChange_Response=>Disconnect: " + ex.toString());
				}
				lsClient.Disconnect(CONNECTION_STATUS.CLOSE_BY_CONNECTION_DROP);					
			}
		}
		
		prevConnectType = iConnectType;
		
		if (NOT_APP_EXIT_OPTION)
		{
			if (isActive == false)
				return;
		}

		//Broadcast for Light Streamer connection parties
		if ((iConnectType == CONNECT_TYPE.WIFI) || (iConnectType == CONNECT_TYPE.GRPS_DATA) 
				|| (iConnectType == CONNECT_TYPE.MOBILE_DATA))
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "New Network type for connection.");
			
		    //Allow the thread to run
			if (cdrItemReporter != null)
				cdrItemReporter.ResumeProcess();
			
			if (LSClient_ShouldRetry())
			{
				//A sudden change of network might use the wrong path from previous logon
				isLSConnecting = false;
				if (isActive)
					ReLogonToServer(null);
			}					
		}			
		else if (iConnectType == CONNECT_TYPE.NONE)
		{
            	//Let the thread to stop
			if (cdrItemReporter != null)
				cdrItemReporter.PauseProcess();
		}
			
		synchronized (networkListLock)
		{
			Message newMessage;

			newMessage = new Message();
			newMessage.what = UPDATE_NETWORK_CALLER;			
			for (INetworkChangeListener item : networkListenerList)
			{
				newMessage.obj = item;
				localMsgHandler.sendMessage(newMessage);					
			}
		}
	}

	// ********** Screen Orientation Related **********
	// If it is the identical, it will be replaced, One registration is allowed
	// for one context
	/** Register to Listen for Screen Orientation Change,PROTRAIL or LANDSCAPE
	  * to use this, the config change of the activity under Manifest should be set to turn off
	  * The report is in UI thread.
	  * RegisterToListenScreenOrientationChange and UnregisterToListenScreenOrientationChange, works in pair
	  * If it is the identical, it will be replaced, One registration is allowed
	  * for one context 
	  * @param  iActivity - instance with implement of IScreenOrientationChangeListener
	  * @return true for successfully register, false for if it is already in the list
	  */ 	 	
	public boolean RegisterToListenScreenOrientationChange(
			IScreenOrientationChangeListener iActivity)
	{
		if (iActivity == null)
			return false;

		// We have no sensor
		if (DeviceInfo.IsOrientationSensorAvailable(this) == false)
		{
			TDStockLog.i(GetIdentity(),
					"RegisterToListenScreenOrientationChange: No Sensor is available");
			return false;
		}

		boolean status, wasEnable;		
		synchronized(scrOrientationListLock)
		{		
			if (scrOrientationListenerList.contains(iActivity))
				return true;

			wasEnable = scrOrientationListener.IsEnabled();
			status = scrOrientationListenerList.add(iActivity);
		}
		
		if (wasEnable)
		{
			Message newMessage;

			newMessage = new Message();
			newMessage.what = UPDATE_ORIENTATION_CALLER;
			newMessage.obj = iActivity;
			localMsgHandler.sendMessage(newMessage);
		}
		else
			scrOrientationListener.Enable();

		return status;
	}
	
	/** Unregister to Listen for Screen Orientation Change,PROTRAIL or LANDSCAPE
	    RegisterToListenScreenOrientationChange and UnregisterToListenScreenOrientationChange, works in pair
	  * @param  iActivity - instance with implement of IScreenOrientationChangeListener
	  */ 		
	public boolean UnregisterToListenScreenOrientationChange(
			IScreenOrientationChangeListener iActivity)
	{
		if (iActivity == null)
			return false;

		synchronized(scrOrientationListLock)
		{
			int index;
		
			index = scrOrientationListenerList.indexOf(iActivity);
			if (index != -1)		
				scrOrientationListenerList.remove(index);
			if (scrOrientationListenerList.size() == 0)
				scrOrientationListener.Disable();			
		}
		return true;
	}
	
	/** Get the Current Screen Orientation, Result is different from the system
	 *  @return SCR_ORIENTATION enum (PORTRAIT, LANDSCAPE, UNKNOWN if not being registered before
	 */
	public SCR_ORIENTATION GetCurrentOrientation()
	{
		if (debugMode)
			TDStockLog.i(GetIdentity(), "GetCurrentOrientation: "
					+ scrOrientationListener.GetScreenOrientation());

		return scrOrientationListener.GetScreenOrientation();
	}

	/** Report of orientation change, Return in a UI thread
	  * ** Means to be used internally by the application only
	  */
	@Override
	public void ScreenOrientation_Response(SCR_ORIENTATION iOrientation)
	{
		synchronized(scrOrientationListLock)
		{
			Message newMessage;
			
			newMessage = new Message();
			newMessage.what = UPDATE_ORIENTATION_CALLER;
			for (IScreenOrientationChangeListener item : scrOrientationListenerList)
			{
				newMessage.obj = item;
				localMsgHandler.sendMessage(newMessage);					
			}			
		}
	}
 
	// ********** Light Streamer Connection Status Reported Related **********
	// If it is the identical, it will be replaced, One registration is allowed
	// for one context		// Storage class for Light Streamer Request Connection 

	/** Register to listen to the light streamer connection
	  * RegisterToListenLSClientConnection and UnregisterToListenLSClientConnection works in pairs
	  * @param iActivtyClass - instance with implementation ILS_UserClient_ConnectionResponse, where to report
	  */
	public boolean RegisterToListenLSClientConnection(ILS_UserClient_ConnectionListener iActivtyClass)
	{
		if (iActivtyClass == null)
			return false;
	
		// Update the caller once no matter what		
		synchronized(lsConnectStatusListLock)
		{
			if (lsConnectStatusList.contains(iActivtyClass))
				return true;

			lsConnectStatusList.add(iActivtyClass);			
		}
		//modified by haibo in 2011-10-20, fixed  jcweb  logon  issue		
		//Requires to update interest party about current status
		CONNECTION_STATUS curStatus;
		
		curStatus = LSClient_GetLastConnectionStatus();
		/* This is the initial setting of lightstreamer, we could neglect to report to user */
		//Andy, add extra checking to diminish the displaying of wrong status
		if ((curStatus != CONNECTION_STATUS.NOT_REQUEST) && (curStatus != CONNECTION_STATUS.CLOSE_ON_REST) 
			&& (curStatus != CONNECTION_STATUS.CLOSE_BY_REQUEST))
			iActivtyClass.LS_UserClient_ConnectionReport(curStatus);
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_ConnectionRegister: " + iActivtyClass.getClass().toString());	
		
		return true;		
	}
	
	/** Unregister to listen to the light streamer connection
	  @ RegisterToListenLSClientConnection and UnregisterToListenLSClientConnection works in pairs
	  * @param iActivityClass - registered activity class
	  */
	public boolean UnregisterToListenLSClientConnection(ILS_UserClient_ConnectionListener iActivityClass)
	{		
		if (iActivityClass == null)
			return false;
		
		synchronized(lsConnectStatusListLock)
		{
			int index;
			
			index = lsConnectStatusList.indexOf(iActivityClass);
			if (index != -1)
			{
				lsConnectStatusList.remove(index);
				if (debugMode)
					TDStockLog.i(GetIdentity(), "LSClient_ConnectionUnRegister: " + iActivityClass.getClass().toString());
			}				
		}
		return true;
	}	

	/** Ask whether light streamer is connected 
	 * @return true for connected 
	 */
	public boolean LSClient_IsConnected()
	{
		return lsClient.IsConnected();
	}

	/** Get the last report connection status of light streamer
	 *  @return CONNECTION_STATUS enum
	 */	
	public CONNECTION_STATUS LSClient_GetLastConnectionStatus()
	{
		return lsClient.GetLastConnectStatus();
	}
	
	private CONNECTION_STATUS prevStatus = CONNECTION_STATUS.NOT_REQUEST;
	/** Connection Report
	  * ** Means to be used internally by the application
	 */
	@Override
	public void LS_UserClient_ConnectionReport(CONNECTION_STATUS iStatus)
	{
		if (prevStatus == iStatus)
			return;
		
		prevStatus = iStatus;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LS_UserClient_ConnectionReport=>" + iStatus + ", isActive=" + isActive);
		
		// LS Connecting status Changed, Switching meaning from Delay to RealTime
		// or RealTime to Delay
		if (iStatus != CONNECTION_STATUS.CLOSE_BY_SWITCHING)
			isLSConnecting = false;
		
		//LS Disconnecting status Changed
		if (iStatus != CONNECTION_STATUS.CONNECTED)
			isLSDisconnecting = false;
		else
		{			
			//Save the last access time
			if (chargingRecord != null)
				chargingRecord.SetLastAccessTime();
		}

		//Just give it a try no matter what 
		if (iStatus == CONNECTION_STATUS.CONNECTED)		
			InternalSubscribeItems();			
		
		if (isActive == false)			
			return;			
		
		//Try to re-register for our charging flag
		if (iStatus == CONNECTION_STATUS.CONNECTED)
			LSClient_ResumeAllItems(GetCurrentActivityClass());
		else if (iStatus == CONNECTION_STATUS.CLOSE_BY_CONNECTION_DROP)
		{
			//Maybe we should try to reconnect
			if (networkListener.GetConnectionType() != CONNECT_TYPE.NONE)
			{
				if (isActive)
				{
					if (debugMode)
						TDStockLog.i(GetIdentity(), "A sudden drop from light stream but has network.  Perform auto-retry");
					try
					{
						Thread.sleep(1000);
					}
					catch(Exception ex)
					{
						TDStockLog.e(GetIdentity(), "LSClient_ResumeAllItems: Thread Sleep fail");
					}
					
					if (!isLSConnecting)
						ReLogonToServer(null);
				}
			}				
		}
		
		if (onTermianting == false)
		{
			Message newMessage;
			
			newMessage = new Message();
			newMessage.what = UPDATE_LS_CONNECT_CALLER;
			synchronized(lsConnectStatusListLock)
			{
				for (ILS_UserClient_ConnectionListener item : lsConnectStatusList)
				{
					newMessage.obj = item;
					localMsgHandler.sendMessage(newMessage);					
				}
			}				
		}		
	}	
	
	// ***************** End Designed Listeners API ***************************
	
	/** Request for Free Trail, a reply of successful send and other will be provided
	  * Once it is success, it will auto-login under light streamer
	  * @param iUserName
	  * @param iMobileNumber - for SMS service for the Access Code
	  * @param iEmail
	  * @param iResponse - ITDStockReqFreeTrial_Result interface to receive result 
       */
	public void LS_RequestFreeTrial(String iUserName, String iMobileNumber,
							  String iEmail, ITDStockReqFreeTrial_Result iResponse)
	{
		synchronized (padLock)
		{
			TDStockReqFreeTrial reqFree;
			String ulrString;

			ulrString = LS_ClientParamMemory.Get_ReqFreeTrialURL(this, 
							iUserName, iMobileNumber,iEmail);
			reqFree = new TDStockReqFreeTrial(this, ulrString,
										LS_ClientParamMemory.Get_RegFreeTrialPort());
			reqFree.SendRequest(iResponse);
		}
	}
	//by zor
	/** Use the login and password from user input to perform the same feature as with our Activation code
	 *  @param iActivateCode - access code obtain from Request for Free Trail
	 *  @param iAuthRequest  - reply in an interface of ILS_UserClient_Authentication_Report
	 */
	public boolean LS_RequestFreeTrialActivate(String iActivateCode,
			ILS_UserClient_Authentication_Report iAuthRequest)
	{		
		if (unlockScreenConnectOption == false)
		{
			if (isActive == false)
				return false;
		}
		
		synchronized (padLock)
		{
			//Do not wast time if there are no network
			if (IsNetworkAvailable() == false)
				return false;
			
			if (isLSConnecting)
				return true;				
				
			String urlString;

			urlString = LS_ClientParamMemory.Get_RequestFreeTrialActivateURL(this, iActivateCode);
			// We have the string, we could use the normal authentication now
			authResponse      = iAuthRequest;
			freeActivateCode  = iActivateCode;
			usingFreeTrialCode = true;
			
			isLSConnecting = true;
			LS_ClientParamMemory.SetDelayMode(false);
			LS_ClientParamMemory.SetLogonModeType(LS_ClientParamMemory.ModeType.ACTIVATECODE);
			LS_ClientParamMemory.SetBMPMode(lsClient.IsBMPMode());
			lsClient.RequestSessionKey(urlString, LS_ClientParamMemory.Get_RequestFreeTrialActivatePort(), usingFreeTrialCode, this);
		}
		return true;
	}

	/** Verify whether this device is still could use the free trial by looking at the device ID **/
	public void LS_RequestFreeTrialVerifiy( ITDStockReqFreeVerify_Result iResponse)
	{
		synchronized (padLock)
		{
			TDStockReqFreeVerify reqFree;
			String ulrString;

			ulrString = LS_ClientParamMemory.Get_ReqFreeVerifyURL(this);
			reqFree = new TDStockReqFreeVerify(this, ulrString,
						LS_ClientParamMemory.Get_ReqFreeVerifyPort());
			reqFree.SendRequest(iResponse);
		}
	}
	
	// ******* Light Streamer API *******
	//Storage class for Light Streamer Request Item	
	static private class LS_SubReqItem
	{
		//Happen mostly on Stock price info
//		private static final String[] paidSchemaText 	   = new String[]{ "BidPrice", "AskPrice", "Nominal", "Net" };
		private static final String[] paidSchemaText 	   = new String[]{ "Nominal" };		
		//Only Stock has this option
		private static final String[] paidSchemaPartialText  = new String[] {"ASK_QUEUE", "BID_QUEUE"};	
		
		//Happen mostly on HSI Info, Obsolete
//		private static final String[] paidItemText 	        = new String[]{ "HSI"};		
		
		public  Class<? extends Activity>	ownerActivityClass;
		public  String					identifier;
		public  LS_SubscriptionItem         reqItem;
		public  LS_ItemRequestType         chargeType;
		public  ILS_UserClient_Response	responseHandler;

		/** Request Item storage **/		
		// Auto
		static public LS_ItemRequestType CheckLSSubscriptionItem(LSStringList iItemValue, LSStringList iSchemaValue)
		{
			int index, count;
			int subCount;

			count = iSchemaValue.GetSize();
			if (count == 0)
				return LS_ItemRequestType.NONE;

			//Looking for HSI keyword, Obsolete
//			subCount = paidItemText.length;
//			for (index = 0; index < subCount; index++)
//			{
//				if (iItemValue.ContainsAny(paidItemText[index]))
//					return LS_ItemRequestType.BOOKMARK_CHARGE;
//			}			
			
			//Looking for the Buy, Sell queue keyword, has higher priority
			subCount = paidSchemaPartialText.length;			
			for (index = 0; index < subCount; index++)
			{
				if (iSchemaValue.ContainsAny(paidSchemaPartialText[index]))
					return LS_ItemRequestType.NORMAL_CHARGE;
			}

			subCount = paidSchemaText.length;
			for (index = 0; index < subCount; index++)
			{
				if (iSchemaValue.ContainsAny(paidSchemaText[index]))
					return LS_ItemRequestType.BOOKMARK_CHARGE;
			}
			
			
//			subCount = paidSchemaText.length;
//			for (index = 0; index < subCount; index++)
//			{
//				if (iSchemaValue.Contains(paidSchemaText[index]))					
//					return LS_ItemRequestType.NORMAL_CHARGE;
//			}
			
			return LS_ItemRequestType.NONE;
		}
		
		/** Request Item storage
		 * @param iClassOwner      - Activity Owner
		 * @param iIdentifier      - identification for the response
		 * @param iItem            - storage for subscription item
		 * @param iChargeType      - enforced charge type for this subscription
		 * @param iResponseHandler - callback for the owner
		 */		
		public LS_SubReqItem(Class<? extends Activity> iClassOwner,
				  String iIdentifier, LS_SubscriptionItem iItem, 
				  LS_ItemRequestType iChargeType, 
				  ILS_UserClient_Response iResponseHandler)
		{
			ownerActivityClass        = iClassOwner;
			identifier      = iIdentifier;
			reqItem         = iItem;
			responseHandler = iResponseHandler;
			chargeType      = iChargeType;
		}		
	}
	
	@SuppressWarnings("unused")
	private List<LS_SubReqItem> GetAllChargeItemsFromRequestList()
	{
		List<LS_SubReqItem> replyList;
		
		replyList = new ArrayList<LS_SubReqItem>();
		synchronized(reqListLock)
		{
			for (LS_SubReqItem perItem: lsReqItemList)
			{
				if (perItem.chargeType != LS_ItemRequestType.NONE)
					replyList.add(perItem);
			}
		}					
		return replyList;
	}
	
	@SuppressWarnings("unused")
	private List<LS_SubReqItem> GetRequestTypeOnList(LS_ItemRequestType iType)
	{
		List<LS_SubReqItem> replyList;
		
		replyList = new ArrayList<LS_SubReqItem>();
		synchronized(reqListLock)
		{
			for (LS_SubReqItem perItem: lsReqItemList)
			{
				if (perItem.chargeType == iType)
					replyList.add(perItem);
			}
		}			
		return replyList;
	}	
	
	private boolean IsAnyChargeItemsOnRequestList()
	{
		synchronized(reqListLock)
		{
			for (LS_SubReqItem perItem: lsReqItemList)
			{
				if (perItem.chargeType != LS_ItemRequestType.NONE)
					return true;
			}
		}			
		return false;
	}

	private boolean IsAnyRequestTypeOnList(LS_ItemRequestType iType)
	{
		synchronized(reqListLock)
		{
			for (LS_SubReqItem perItem: lsReqItemList)
			{
				if (perItem.chargeType == iType)
					return true;
			}
		}			
		return false;		
	}
	
	
	private Object                reqListLock = new Object();
	private List<LS_SubReqItem>	lsReqItemList;	
	private LS_UserClient		lsClient;
	private String                userLogonName;
	private String                lastUserLogonName;
	private String                userLogonPassword;
	private String                freeActivateCode;
     private boolean               usingFreeTrialCode;
	private List<ILS_UserClient_ConnectionListener>  lsConnectStatusList;
	private Object                                   lsConnectStatusListLock = new Object();
	
	/** Use to tell the system that light streamer is connection, the subscription could be added
	 * to list.  This is to prevent for multiple connection request */
	private boolean isLSConnecting;
	
	/* Design to prevent collision on multiple connection request */
	public boolean LSClient_IsConnecting()
	{
		return isLSConnecting;
	}
	
	/** Create storage for LS Item register **/	
	private void LSClient_Create(Context iContext)
	{
		lsClient    = new LS_UserClient(iContext, this);		
		lsReqItemList = new ArrayList<LS_SubReqItem>();
		// New list for connect status
		lsConnectStatusList = new ArrayList<ILS_UserClient_ConnectionListener>();
		cdrListenerList     = new ArrayList<IReporterTicker_Report>();
		userLogonName     = null;
		lastUserLogonName = null;
		userLogonPassword = null;
		freeActivateCode  = null;
		usingFreeTrialCode = false;
		isLSConnecting     = false;
	}

	/** Reset the connection parameter for the application layer **/
	public void LSClient_Reset()
	{
		userLogonName     = null;
		userLogonPassword = null;
		freeActivateCode  = null;
		usingFreeTrialCode = false;
		if (lsClient.IsConnected())
			lsClient.Disconnect(CONNECTION_STATUS.NOT_REQUEST);
	}
	
	private ILS_UserClient_Authentication_Report	authResponse;
	private final int disconnectDelayWait = 1400;
	
	/** Custom login to the Light Streamer server.  If a connection is established or on connect,
	    it will be disconnected.  This procedure is to obtain session key and account number
	    require url, login, password, view, language, location, device id, device
	    type **/	
	public boolean LSClient_Logon( String iUserName, String iPassword, String prefDeviceID, 
						      ILS_UserClient_Authentication_Report iAuthRequest)
	{		
		if (unlockScreenConnectOption == false)
		{
			if (isActive == false)
				return false;
		}
		
		synchronized (padLock)
		{				
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_Logon: Login Enter");
			
			if ((iUserName == null) || (iPassword == null))
				return false;

			if ((iUserName.length() == 0) || (iPassword.length() == 0))
				return false;			
			
			if (!IsNetworkAvailable())
				return false;
			
			if (isLSConnecting)
			{						
				if (debugMode)
					TDStockLog.i(GetIdentity(), "LSClient_Logon: Already in progress");				
				return true;
			}				
									
		 	String loginRequestURL;
		 	
			loginRequestURL = LS_ClientParamMemory.Get_AuthenticationURL(this, iUserName, iPassword);
			if ((prefDeviceID != null) && (prefDeviceID.length() > 0))
				loginRequestURL += "&pdevice_id=" + prefDeviceID;

			userLogonName     = iUserName;
			userLogonPassword = iPassword;
			authResponse      = iAuthRequest;
			usingFreeTrialCode = false;
			
			isLSConnecting     = true;
									
			if (userLogonName.equals(LS_ClientParamMemory.DemoLogon))
			{
				isChargeTradingHour = false;
				isChargeTradingHourBookMark = false;
				LS_ClientParamMemory.SetDelayMode(true);
				LS_ClientParamMemory.SetLogonModeType(LS_ClientParamMemory.ModeType.TDLDEMO);
			}
			else
			{
				LS_ClientParamMemory.SetDelayMode(false);
				LS_ClientParamMemory.SetLogonModeType(LS_ClientParamMemory.ModeType.ACCOUNTLOGIN);
			}				
			//Reset the BMPMode
			LS_ClientParamMemory.SetBMPMode(false);				
						
			if (lsClient.IsConnected())
			{
				lsClient.ClearAllSubscribeItems();					
				try
				{				
					Thread.sleep(disconnectDelayWait);					
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();					
					TDStockLog.e(GetIdentity(), "LSClient_Logon: " + e.toString());
				}
				//This announced a message to all user					
				lsClient.Disconnect(CONNECTION_STATUS.CLOSE_BY_SWITCHING);				
			}
			isLSConnecting = true;
			lsClient.RequestSessionKey(loginRequestURL, LS_ClientParamMemory.Get_AuthenticationPort(), usingFreeTrialCode, this);
		}	
		return true;
	}
	
	/** Custom login to the Light Streamer server.  If a connection is established or on connect,
	    it will be disconnected.  This procedure is to obtain session key and account number
	    require url, login, password, view, language, location, device id, device
	    type **/	
	public boolean LSClient_Logon(String iUserName, String iPassword,
			ILS_UserClient_Authentication_Report iAuthRequest)
	{		
		return LSClient_Logon(iUserName, iPassword, null, iAuthRequest);	
	}

	/** Logon using internal saved data, to obtain key and account and auto logon **/
	private boolean ReLogonToServer(ILS_UserClient_Authentication_Report iResponse)
	{		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "ReLogonToServer Enter");
				
		if (unlockScreenConnectOption == false)
		{
			if (isActive == false)
				return false;
		}
		
		if (!IsNetworkAvailable())
			return false;
		
		if (usingFreeTrialCode)
		{
			if ((freeActivateCode == null) || (freeActivateCode.length() == 0))
			{
				if (debugMode)
					TDStockLog.i(GetIdentity(), "Free Trial ActivateCode not exist");
				return false;
			}
			
			LS_RequestFreeTrialActivate(freeActivateCode, iResponse);
			return true;
		}
		
		if ((userLogonName == null) || userLogonPassword == null)
			return false;
		
		if ((userLogonName.length() == 0) || userLogonPassword.length() == 0)
			return false;		
		
		LSClient_Logon(userLogonName, userLogonPassword, iResponse);
		return true;
	}
	
	/** Internal handling for login result.  Result in report with UI thread
	    Successful result will make a auto-login to light streamer server,
	    else a negative response will be passed to the requester.
	    The UserName and password will be memorized to serve for the next auto-login **/
	@Override
	public void LS_UserClient_Authentication_Response( LS_UserClient_Authentication_Result iResponse)
	{		
		CONNECTION_STATUS connectStatus;		
		
		connectStatus = CONNECTION_STATUS.CONNECTION_FAIL;
		// Try to perform an auto connect to light streamer server
		if (iResponse.successfulSend)
		{		
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LS_UserClient_Authentication_Response: Message sent with reply.");
			
			if (iResponse.statusCode != null)
			{
				if (iResponse.statusCode.equals("0"))
				{		
					lastUserLogonName = userLogonName;
					if (debugMode)
						TDStockLog.i(GetIdentity(), "LS_UserClient_Authentication_Response: Perform Auto Logon");	
					
					if (lsClient.IsConnected())
					{
						lsClient.ClearAllSubscribeItems();						
						try
						{
							Thread.sleep(disconnectDelayWait);
						}
						catch (InterruptedException e)
						{
							TDStockLog.e(GetIdentity(), "LS_UserClient_Authentication_Response: " + e.toString());
							e.printStackTrace();
						}
						lsClient.Disconnect(CONNECTION_STATUS.CLOSE_BY_SWITCHING);						
					}
												
					synchronized(padLock)
					{
						//Set as BMPMode
						LS_ClientParamMemory.SetBMPMode(lsClient.IsBMPMode());	
						connectStatus = lsClient.RequestConnection(  LS_ClientParamMemory.Get_ClientConnectURL(), 
															LS_ClientParamMemory.Get_ClientConnectPort(),
															LS_ClientParamMemory.Get_ClientConnectAdapter());
					}
				}
				else if(iResponse.statusCode.equals("4"))  /* Invalid Logon name */
					connectStatus = CONNECTION_STATUS.CONNECTION_INV_USER;
				else if (iResponse.statusCode.equals("5")) /* Invalid Logon password */
					connectStatus = CONNECTION_STATUS.CONNECTION_INV_PASSWD;
				else
					connectStatus = CONNECTION_STATUS.CONNECTION_REJECT;				
			}
			else
				connectStatus = CONNECTION_STATUS.CONNECTION_REJECT;
		}
		/* Stop the connection status */
		isLSConnecting = false;

		//Update the one with Authentication
		Message msg;

		msg = new Message();
		msg.what = UPDATE_AUTHENTICATION_CALLER;
		msg.obj = iResponse;
		localMsgHandler.sendMessage(msg);

		//When connect, the LS_User Client will prompt will result
		if (connectStatus != CONNECTION_STATUS.CONNECTED)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LS_UserClient_Authentication_Response=>Spreadout: " + connectStatus);
			LS_UserClient_ConnectionReport(connectStatus);
		}		
	}

	/** Check whether the Light Streamer is connected to Horse info or Stock Info **/
	public INFO_MODE LSClient_GetInformationMode()
	{
		return LS_ClientParamMemory.GetInformationMode();
	}
	
	/** Enforce a connection with defined login information. If information
	 * is missing, a return of false
	 * @return
	 */
	public boolean LSClient_Connect()
	{
		return LSClient_Connect(null);
	}
	
	/** Enforce a connection with defined login information. If information
	 * is missing, a return of false
	 * @param iResponse - a eligible response of notification
	 * @return
	 */
	public boolean LSClient_Connect(ILS_UserClient_Authentication_Report iResponse)
	{		
		if (lsClient.IsConnected())
			return true;
		
		return ReLogonToServer(iResponse);
	}
	
	public boolean LS_Client_ResetConnection()
	{
		return ReLogonToServer(null);
	}
	
	/** Enforce a disconnection of light streamer client *
	 *  @return false might mean it is already disconnect
	 */
	public boolean LSClient_Disconnect()
	{
		if (isLSDisconnecting)
			return true;
		
		CONNECTION_STATUS curStatus;
		
		curStatus = LSClient_GetLastConnectionStatus();
		if ((curStatus != CONNECTION_STATUS.CONNECTED) && (curStatus != CONNECTION_STATUS.NOT_REQUEST))
		{
			lsClient.OverrideDisconnectStatus(CONNECTION_STATUS.CLOSE_BY_REQUEST);
			return true;
		}			

		/* Wrap up any CDR */
		//Send a CDR Report		
		if (chargingRecord != null)
		{
			String reportString;
		
			chargingRecord.SetLastAccessTime();			
			reportString = FinalizeChargingRecord();
			if (reportString.equals("") == false)
				cdrItemReporter.AddURLToAdvanceStorage(reportString, IMMEDIATE_SEQ.TOP);
			
			chargingRecord = null;
		}
		
		if (chargingTimer != null)
		{
			chargingTimer.Dispose();
			chargingTimer = null;
		}
		
		LSClient_Disconnect(CONNECTION_STATUS.CLOSE_BY_REQUEST);

		return true;
	}

	private boolean isLSDisconnecting = false;
	/** Propose a disconnect command using a CONNECTION_STATUS Request **/
	private void LSClient_Disconnect(CONNECTION_STATUS iCloseRequest)
	{
		if (isLSDisconnecting)
			return;
		
		if (iCloseRequest == CONNECTION_STATUS.CLOSE_BY_FORCE)
			lsReqItemList.clear();
		
		isLSDisconnecting = true;
		
		final CONNECTION_STATUS closeReqStatus;
			
		closeReqStatus = iCloseRequest;
		//Suspend all connection in our list when user call
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_Disconnect: Request Connection to " + iCloseRequest);

		Thread internalThreadObj = new Thread()
		{
			public void run()
			{
				//Block communication when being used					
				synchronized(padLock)
				{					
					//If it is disconnect already, then we should just have the isLSDisconnect
					if (lsClient.Disconnect(closeReqStatus) == false)
						isLSDisconnecting = false;
				}						
			}
		};
		internalThreadObj.start();		
	}
	
	/** Obtain the current session key for light streamer connection
	 *  @return sessionKey for login 
	 */
	public String GetLSClient_SessionKey()
	{
		return lsClient.GetSessionKey();
	}
	
	/** Obtain the Account name that use for light streamer
	 * @return
	 */
	public String GetLSClient_AccountName()
	{
		return lsClient.GetAcctName();
	}

	/** Obtain the Light Streamer Logon name **
	 * @return light streamer logon name with "hk" / "trial"
	 */
	public String GetLSClient_AccountLogonName()
	{
		return lsClient.GetAcctLogonName();
	}
	
	
	/** Obtain the ServiceFlag that is from request login
	 * @return Service Flag from User login
	 */
	public String GetLSClient_ServiceFlags()
	{
		return lsClient.GetServiceFlags();
	}
	
	/** Obtain the ServiePlan that is from request login
	 * @return Service Flag from user login
	 */
	public String GetLSClient_ServicePlan()
	{
		return lsClient.GetServicePlan();
	}
	
	/** Obtain the Horse Message that is from request login
	 * @return Horse Promotion message
	 */
	public String GetLSClient_HorseMessage()
	{
		return lsClient.GetHorseMessage();
	}
	
	/** Obtain the Sport Message that is from request login
	 * @return Sport Promotion Message
	 */
	public String GetLSClient_SportMessag()
	{
		return lsClient.GetSportMessage();
	}
	
	public String GetLSClient_ErrorMessage()
	{
		return lsClient.GetErrorMessage();
	}
	
	/** Obtain the user name for Requesting Light Streamer Session key
	 *  The login name on your request connection page
	 * @return user name in string, if it is a testing mode, a unique name reply, if it is a delay mode, a unique name replies
	 */
	public String GetLSClient_UserName()
	{
		// 試用版		
		if (usingFreeTrialCode)
			return getString(R.string.TrialMode_Text);
		
		//延遲版		
		if (IsUsingDelayMode())
			return getString(R.string.DelayMode_Text);		
		
		if (userLogonName == null)
			return "";
				
		return userLogonName;	
	}

	/** Ask if we are in demo mode
	  * DelayMode == Logon as tdldemo
	  * From our internal checking based on user name
	  * @return true for using delay mode 
	  */
	public boolean IsUsingDelayMode()
	{
		if (userLogonName == null)
			return false;
		
		if (userLogonName.equals(LS_ClientParamMemory.DemoLogon))
			return true;
		
		return false;
	}
	
	/** Ask whether this app is using Development server **/
	public boolean IsDevelopment()
	{
		return LS_ClientParamMemory.IsDevelopment();
	}
	
	/** Ask the light streamer whether we are on delay mode
	  *  DelayMode == Log-on as tdldemo
	  *  @return reply from our registry under LS client
	  */
	public boolean IsDelayMode()
	{
		return LS_ClientParamMemory.IsDelayMode();
	}	
	
	public LS_ClientParamMemory.ModeType GetLogonModeType()
	{
		return LS_ClientParamMemory.GetLogonModeType();
	}
	
	public boolean IsBMPMode()
	{	
		return LS_ClientParamMemory.IsBMPMode();
	}
	
	
	/* Try to obtain the object from the list based on the iIdentifier
	 * @param iIdentifier - the searching name
	 * @return  either null or the object
	 */
	private LS_SubReqItem GetLSReqItemFromList(String iIdentifier)
	{
		synchronized(reqListLock)
		{
			for (LS_SubReqItem subItem: lsReqItemList)
			{
				if (iIdentifier.equals(subItem.identifier) == true)
					return subItem;			
			}
		}
		return null;
	}

	/* The sequence number of the identifier for subscription item
	 * @param iIdentifier - unique identifier for subscription
	 * @return -1 for not found, >= 0 for the index of the list
	 */
	private int IndexOfID_LSReqItemList(String iIdentifier)
	{
		int index;
		
		index = -1;
		synchronized(reqListLock)
		{
			for (LS_SubReqItem subItem: lsReqItemList)
			{
				++index;
				if (iIdentifier.equals(subItem.identifier) == true)
					return index;
			}
		}
		return -1;
	}

	public boolean IsAnyItemsExist(Class<? extends Activity> iOwnerClass)
	{
		synchronized(reqListLock)
		{
			for (LS_SubReqItem iItem : lsReqItemList)
			{
				if (iItem.ownerActivityClass.equals(iOwnerClass))
					return true;
			}
		}
		return false;		
	}
	
	/** Resume all Item from the list within the owner class, if items is already request, then it would reply false
	 *  @param iOwnerClass - a registered owner class for subscription
	 *  @return true if subscription is done, false if not
	 */
	public boolean LSClient_ResumeAllItems(Class<? extends Activity> iOwnerClass)
	{		
		if (iOwnerClass == null)
			return true;
		
		if (screenIsLocked)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "Detect screen is locked, LSClient_ResumeAllItems will not proceed");
			return false;
		}

		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_ResumeAllItems starts");

		if (IsNetworkAvailable() == false)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_ResumeAllItems: No Network, we can not resume");
			return false;
		}		
		
		final List<String> 	     	 idList;
		final List<LS_SubscriptionItem> itemList;
		
		/* Filter to set the list as the one with the same class */
		idList   = new ArrayList<String>();
		itemList = new ArrayList<LS_SubscriptionItem>();		
				
		synchronized(reqListLock)
		{
			for (LS_SubReqItem iItem : lsReqItemList)
			{
				if (iItem.ownerActivityClass.equals(iOwnerClass) == false)
					continue;
			
				idList.add(iItem.identifier);
				itemList.add(iItem.reqItem);
			}
		}			
		
		if ((idList.size() == 0) || (itemList.size() == 0))
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_ResumeAllItems: End=>No items to resume subscription");
			return true;
		}
		
		//Add the charge on CDR
		ApplyChargingScheme();
		
		//THe setList feature is already running in a thread.  So, we do not need to span a new thread.
		if (LSClient_IsConnected())
			return LSClient_SetListItem(idList, itemList);
		
		//We are not sure it is ontime drop or retry drop, so put some delay in case of collision
		if (LSClient_ShouldRetry())
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_ResumeAllItems: Attempt for reconnection");
			
			return ReLogonToServer(null);
		}
		return true;
	}
	
	private boolean LSClient_SetListItem(List<String> idList, List<LS_SubscriptionItem> itemList)
	{
		int count;

		count = idList.size();
		if (count == 0)
			return false;
		
		if (count != itemList.size())
			return false;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_SetListItem: Subscribe Action. Item Cout=" + count);	
		
		return lsClient.SubscribeItemInBatch(idList, itemList);
	}
		
	private void LSClient_RemoveListItems(final List<String> iListItem)
	{	
		if (iListItem.size() == 0)
			return;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_RemoveListItems: Clear list action");
		
		// will not check connection, but will un-subscribe from the list
		lsClient.ClearIdentifierList(iListItem);
	}
	
	/** Suspend on item on the list, the item will stay in our list, 
	 *  but un-subscription will be performed, un-subscribe takes certain amount of time, which
	 *  might block the UI for a while
	 *  @param iOwnerClass - a registered owner class for subscription
	 *  @return true if is success  
	 */
	public boolean LSClient_SuspendAllItems(final Class<? extends Activity> iOwnerClass)
	{		
		final List<String>  idList;
								
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_SuspendAllItems on call");
		
		idList = new ArrayList<String>();	
		synchronized(reqListLock)
		{
			for (LS_SubReqItem iItem : lsReqItemList)
			{
				if (iItem.ownerActivityClass.equals(iOwnerClass) == false)
					continue;
			
				//If it is a background, we would not stop it
				if (iItem.reqItem.IsBackgroundRunnable() == true)
					continue;

				idList.add(iItem.identifier);
			}				
		}
		
		if (idList.size() == 0)
			return true;
		
		/** Gather the required in the list, and now set a thread to delete them **/
		Thread internalThreadObj = new Thread()
		{
			public void run()
			{
				LSClient_RemoveListItems(idList);
			}
		};
		internalThreadObj.start();
		
		return true;
	}
	
	/** Set suspend to all items stored.  The list still exist**/
	public void LSClient_SuspendAllActivityReqItems()
	{
		final List<String>  idList;
			
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_SuspendAllItems on call");
		
		idList = new ArrayList<String>();
		synchronized(reqListLock)
		{
			for (LS_SubReqItem iItem : lsReqItemList)							
				idList.add(iItem.identifier);
		}			
		
		if (idList.size() == 0)
			return;
		
		Thread internalThreadObj = new Thread()
		{
			/** Gather the required in the list, and now set a thread to delete them **/
			public void run()
			{
				LSClient_RemoveListItems(idList);	
			}
		};
		internalThreadObj.start();
	}
	
	private boolean LSClient_ShouldRetry()
	{
		if (lsClient.IsConnected())
			return false;
	
		if (isLSConnecting)
		{
			TDStockLog.i(GetIdentity(), "LSClient_ShouldRetry: already in progress of connecting.");
			return false;
		}

		CONNECTION_STATUS lsStatus;
			
		lsStatus = lsClient.GetLastConnectStatus();
		//If the last Connection is drop by network problem, then we will resume
		if ((lsStatus == CONNECTION_STATUS.CLOSE_BY_CONNECTION_DROP) 
		   || (lsStatus == CONNECTION_STATUS.CLOSE_ON_REST))
			return true;
				
		return false;
	}
	
	/** Subscribe Item to light streamer.  Add to the internal list
	  * and send out as it is free.  LS_ItemRequestType is to be determined by the system
	  * The report will first comes to us, then spread out to the outside.
	  * @param iOwnerClass - caller for this subscription
	  * @param iIdentifier - unique Id for subscription
	  * @param iItem       - subscription item content
	  * @param iResponse   - Call back notification
	  * @return false for already recognized object exist or there is no network
	  */
	public boolean LSClient_SubscribeItem(  Class<? extends Activity> iOwnerClass,  String iIdentifier,
									LS_SubscriptionItem iItem, ILS_UserClient_Response iResponse)
	{
		LS_ItemRequestType  reqType;
			
		reqType = LS_SubReqItem.CheckLSSubscriptionItem(iItem.GetSubscribeItems(), iItem.GetSubscribeSchema());	
				
		return LSClient_SubscribeItem(iOwnerClass, iIdentifier, iItem, reqType, iResponse);
	}

	/** Subscribe Item to light streamer.  Add to the internal list
	    and send out as it is free. 
	    The report will first comes to us, then spread out to the outside
	  * @param iOwnerClass - caller for this subscription
	  * @param iIdentifier - unique Id for subscription
	  * @param iItem       - subscription item content
	  * @param iRequestType - payment type for subscription
	  * @param iResponse   - Call back notification
	  * @return 
	  */ 	    
	public boolean LSClient_SubscribeItem( Class<? extends Activity> iOwnerClass, String iIdentifier,
								    LS_SubscriptionItem iItem, LS_ItemRequestType iRequestType, 
								    ILS_UserClient_Response iResponse)
	{
		LS_SubReqItem newItem;
		
		//Find duplicate string id item
		if (IndexOfID_LSReqItemList(iIdentifier) != -1)
			return false;
		
		if (debugMode)
		{
			if (iRequestType != LS_ItemRequestType.NONE)
				TDStockLog.i(iOwnerClass.getName(), "Contain reqType="+iRequestType);
		}
		
		newItem = new LS_SubReqItem(iOwnerClass, iIdentifier, iItem, iRequestType, iResponse);
		//First add to out subscribe list
		synchronized (reqListLock)
		{			
			/* Add to the list no matter what */
			lsReqItemList.add(newItem);
		}
		
		if (IsNetworkAvailable() == false)
			return false;
		
		if (iRequestType != LS_ItemRequestType.NONE)
			ApplyChargingScheme();
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LS Connection Status: " + lsClient.GetLastConnectStatus());
		
		//Wait for connect come back to refresh the list
		if (LSClient_IsConnected())
		{
			final LS_SubReqItem importItem; 
						
			importItem = newItem;
			Thread internalThreadObj = new Thread()
			{
				public void run()
				{
					lsClient.SubscribeItem(importItem.identifier, importItem.reqItem);						
				}
			};					
			internalThreadObj.start();			
			return true;				
		}
		
		// Try to reconnect if it is possible, if it is connecting then we should ignore
		if (LSClient_ShouldRetry())
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_SubscribeItem: Attempt for reconnection");
			
			return ReLogonToServer(null);				
		}
			
		return true;		
	}
	
	/** Subscribe Item in a List
	 * @param iOwnerClass
	 * @param iIdList
	 * @param iItemList
	 * @param iResponseList
	 * @return
	 */	
	public boolean LSClient_SubscribeListItems(  Class<? extends Activity> iOwnerClass,
									    List<String> iIdList, List<LS_SubscriptionItem> iItemList, 
									    List<ILS_UserClient_Response> iResponseList)
	{
		int                      index, count;
		List<LS_ItemRequestType> typeList;
		LS_ItemRequestType       type;
		
		count = iItemList.size();
		typeList = new ArrayList<LS_ItemRequestType>();
		
		//Create the typeList for user
		for (index = 0; index < count; index++)
		{
			type = LS_SubReqItem.CheckLSSubscriptionItem(iItemList.get(index).GetSubscribeItems(), iItemList.get(index).GetSubscribeSchema());
			typeList.add(type);		
		}

		return LSClient_SubscribeListItems(iOwnerClass, iIdList, iItemList, typeList, iResponseList);
	}
	
	public boolean LSClient_SubscribeListItems(  Class<? extends Activity> iOwnerClass,
			 							List<String> iIdList, 
									     List<LS_SubscriptionItem> iItemList, 
									     LS_ItemRequestType iSubscribeTypeList,
									     List<ILS_UserClient_Response> iResponseList)
	{
		int                      index, count;
		List<LS_ItemRequestType> typeList;
		
		count = iItemList.size();
		typeList = new ArrayList<LS_ItemRequestType>();
		
		//Create the typeList for user
		for (index = 0; index < count; index++)
			typeList.add(iSubscribeTypeList);
		
		return LSClient_SubscribeListItems(iOwnerClass, iIdList, iItemList, typeList, iResponseList);
	}	
	
	/** Subscribe Item in a List
	 * @param iOwnerClass
	 * @param iIdList
	 * @param iItemList
	 * @param iSubscribeTypeList
	 * @param iResponseList
	 * @return
	 */
	public boolean LSClient_SubscribeListItems(  Class<? extends Activity> iOwnerClass,
									    final List<String> iIdList, 
									    final List<LS_SubscriptionItem> iItemList, 
									    List<LS_ItemRequestType> iSubscribeTypeList,
			 						    List<ILS_UserClient_Response> iResponseList)
	{
		int index, count;

		if (debugMode)
		{
			String debugLine;

			debugLine = "";
			count = iIdList.size();
			for (index = 0; index < count; index++)
				debugLine += iIdList.get(index) + ",";

			TDStockLog.i(GetIdentity(), "LightStreamerSubscribeItemsInBatch: " + debugLine);
		}

		//Check whether the ID has been exist
		count = iIdList.size();

		//Check for duplicate
		for (index = 0; index < count; index++)
		{
			if (IndexOfID_LSReqItemList(iIdList.get(index)) != -1)
				return false;
		}			

		if (debugMode)
			TDStockLog.i(iOwnerClass.getName(), "Contain reqType="+ iSubscribeTypeList.toString());			

		List<LS_SubReqItem> newList;
		LS_SubReqItem       reqItem;

		newList = new ArrayList<LS_SubReqItem>();
		for (index = 0; index < count; index++)
		{
			reqItem = new LS_SubReqItem(iOwnerClass, iIdList.get(index), iItemList.get(index), 
								   iSubscribeTypeList.get(index), iResponseList.get(index));
			newList.add(reqItem);
		}

		synchronized (reqListLock)
		{					
			lsReqItemList.addAll(newList);
		}

		if (newList.size() == 0)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_SubscribeListItems: No items to submit subscribtion");
		}

		if (IsNetworkAvailable() == false)
			return false;

		//Add time to the CDR
		ApplyChargingScheme();	
		//The LSClient_SetListItem is a batch running, run in a thread already
		if (LSClient_IsConnected())
		{
			Thread threadObj = new Thread()
			{
				public void run()
				{
					LSClient_SetListItem(iIdList, iItemList);
				}
			};
			threadObj.start();
			return true;
		}

		// Try to reconnect if it is possible
		if (LSClient_ShouldRetry())
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "LSClient_SubscribeItem: Attempt for reconnection");
			
			return ReLogonToServer(null);				
		}
			
		return true;				
	}
	
	/** Un-subscribe item from light streamer
	  * @param iOwnerClass - owner class for this identification
	  * @param iIdentifier - id for the subscription
	  * @return true for its existence
	  */
 	public boolean LSClient_UnsubscribeItem( Class<? extends Activity> iOwnerClass, String iIdentifier)
	{ 		
		int     index;
		
		index = IndexOfID_LSReqItemList(iIdentifier);
		if (index == -1)
			return false;
		
		synchronized (reqListLock)
		{
			//Restriction to only the owner could un-subscribe
			if (lsReqItemList.get(index).ownerActivityClass.equals(iOwnerClass) == false)
				return false;

			lsReqItemList.remove(index);
		}

		final String removeId;

		removeId = iIdentifier;
		DischargeChargingScheme();

		Thread internalThreadObj = new Thread()
		{
			public void run()
			{				
				if (lsClient.UnsubscribleItem(removeId))
					return;
				
				if (debugMode)
					TDStockLog.i(GetIdentity(), "LSClient_UnsubscribeItem: with no connection, enforce a clear list action");
				lsClient.ClearIdentifier(removeId);				
			}
		};
		internalThreadObj.start();

		return true;		
	}

	/** Auto un-subscribe all items from light streamer based on the owner Context(the Activity belonging)
	  * Runs in a thread, please look for synchronization 
	  * @param iOwnerClass - owner class to un-subscribe
	  * @return true for its existence
	  */
	public boolean LSClient_UnsubscribeAllItems( Class<? extends Activity> iOwnerClass)
	{
		final List<String>   idList;

		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_UnsubscribeAllItems(Batch Mode) on call for " + iOwnerClass.getName());			

		idList = new ArrayList<String>();
		synchronized(reqListLock)
		{
			int           index, count;
			LS_SubReqItem subReqItem;

			count = lsReqItemList.size();
			for (index = 0; index < count; index++)
			{
				subReqItem = lsReqItemList.get(index);
				if (subReqItem.ownerActivityClass.equals(iOwnerClass) == false)
					continue;
				
				//Add to new list and remove it from our list				
				idList.add(subReqItem.identifier);
				lsReqItemList.remove(index);
				--count;
				--index;
			}
		}
				
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_UnsubscribeAllItems for " + idList.size() + " items");

		if (idList.size() == 0)
			return true;

		if (debugMode)
		{
			String tmpString;
			
			tmpString = "LSClient_UnsubscribeAllItems List: ";
			for (String useString: idList)
				tmpString += useString + ",";
		
			TDStockLog.i(GetIdentity(), tmpString);
		}

		DischargeChargingScheme();
 
		Thread internalThreadObj = new Thread()
		{
			public void run()
			{
				LSClient_RemoveListItems(idList);
			}
		};
		
		internalThreadObj.start();	
		
		return true;				
	}
	
	public boolean LSClient_UnsubscribeListItem(Class<? extends Activity> iOwnerClass, List<String> iIdList)
	{
		int            	  index, count, placeId;
		final          	  List<String>   actionList;
	
		count = iIdList.size();
		if (count == 0)
			return true;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "LSClient_UnsubscribeListItem on call");
		
		synchronized (reqListLock)
		{
			if (lsReqItemList.size() == 0)
				return true;

			actionList     = new ArrayList<String>();			
			for (index = 0; index < count; index++)
			{
				placeId = IndexOfID_LSReqItemList(iIdList.get(index));
				if (placeId == -1)
					continue;
				
				if (lsReqItemList.get(placeId).ownerActivityClass.equals(iOwnerClass) == false)
					continue;
					
				actionList.add(iIdList.get(index));
				lsReqItemList.remove(placeId);
			}
		}

		DischargeChargingScheme();
		Thread internalThreadObj = new Thread()
		{
			public void run()
			{				
				LSClient_RemoveListItems(actionList);
			}
		};
		internalThreadObj.start();
		return true;						
	}
	
	/** Get the Index from our internal list about light streamer subscription *
	 *  @param iOwnerClass - ownerClass that has the given iIdentifier
	 *  @param iIdentifier - unique id for the subscription
	 *  @return -1 for not found, >= 0 for a found Index
	 */
	public int LSClient_GetIdentifierIndex( Class<? extends Activity> iOwnClass, String iIdentifier)
	{
		return IndexOfID_LSReqItemList(iIdentifier);
	}
		
	/** LS user Client uses a multi-thread to pass this result, may be we should have a synchronized to perform the same
	    A thread is created, in case there are process takes too long to process **/
	@Override
	public void onUpdate(String iIdentifier, int iSchemaIndex,
			String iSchemaValue, UpdateInfo iUpdateInfo)
	{
		LS_SubReqItem  lsItem;

		//Check for our internal report			
		if (InternalSubscribeItemsReport(iIdentifier, iSchemaIndex, iSchemaValue, iUpdateInfo) == true)
			return;

		synchronized (reqListLock)
		{
			lsItem = GetLSReqItemFromList(iIdentifier);
		}
		
		if (lsItem == null)
			return;

		//Provide information to the running application only.
		//However to safe traffic, we would like to un-subscribe for the user		
		if (lsItem.ownerActivityClass.equals(GetCurrentActivityClass()) == false)
		{
			if (debugMode)
				TDStockLog.i(GetIdentity(), "Not in the list. " 
									 + lsItem.identifier + " of " + lsItem.ownerActivityClass.getName().toString());
			LSClient_UnsubscribeItem(lsItem.ownerActivityClass, iIdentifier);
			return;
		}
		
		ConsumeCDRReport(lsItem);			

		onUpdateExecutation(lsItem, iSchemaIndex, iSchemaValue, iUpdateInfo);	
	}
	
	//Start our CDR calculation logic, check item	
	private void ConsumeCDRReport(LS_SubReqItem iSubItem)
	{	
		if (!IsAnyChargeItemsOnRequestList())
			return;
			
		//Charging should start
		if (!isChargeTradingHour && !isChargeTradingHourBookMark)
			return;
		
		//Start the timer when receiving the data
		if (chargingTimer != null)
			chargingTimer.Start();
		
		//More like a catch up		
		ChargingRecordCreateIfNeed(true);
		if (chargingRecord != null)
		{	
			//Overwrite the time if need
			chargingRecord.SetLightStreamerTimeString(lsClient.GetLSLastConnectTime());
			
			//Remember the incoming data **/
			chargingRecord.SetLastAccessTime();
		}			
	}
	
	private void onUpdateExecutation(
			final LS_SubReqItem iSubSeqObject, final int iSchemaIndex,
			final String iSchemaValue, final UpdateInfo iUpdateInfo)
	{
		Thread threadObj;

		threadObj = new Thread()
		{
			@Override
			public void run()
			{
				try
				{
					iSubSeqObject.responseHandler.onUpdate( iSubSeqObject.identifier, iSchemaIndex,
							                              iSchemaValue, iUpdateInfo);
				}
				catch (Exception ex)
				{
					TDStockLog.e(GetIdentity(), "Error onUpdateExecution: " + iSubSeqObject.identifier + " "
									+ ex.toString());
					ex.printStackTrace();
				}
			}
		};
		threadObj.start();
	}
	
	// *************************** CDR Related Item ******************************
	// Internal Performance, ClientRef0 and ClientRef1 is a unique name
	// Require for the billing process, link to CDR assumption calculation
	private boolean               isChargeTradingHour;          /* Charging enable, higher priority */
	private boolean               isForceDisconnect;            /* Force disconnect by server */
	private boolean               isChargeTradingHourBookMark; /* Bookmark Charing enable */
	
	private final String          disconnectIdentifier  = "ClientRef0";
	private final String          disconnectSchema      = "ForceDisconnect";
	
	private final String          chargeIdentifier       = "ClientRef1";
	private final String          chargeSchema           = "IsChargeRequired";
	private final String          chargeSchema2          = "IsChargeRequired4Bookmark";	
	private final int             ONE_MINUTE             = 60;
	private final int             unlimitecdrStartSecond = ONE_MINUTE;// * 5; 
	private final int             cdrStartSecond         = ONE_MINUTE;
	private final int             cdrIncSecond           = ONE_MINUTE;	
	private final int             cdrMaxSecond           = ONE_MINUTE;
	private LS_SubscriptionPair	disconnectSubscription	= null;
	private LS_SubscriptionPair	isChargedReqSubscription	= null;
	
	private ReporterTicker chargingTimer = null;
	private CDRRecordItem  chargingRecord = null;

	/** Initialize parameter for the charing item **/
	private void InitialInternalSubscribeItemsFlag()
	{
		isForceDisconnect    = false;
		isChargeTradingHour  = false;
		isChargeTradingHourBookMark = false;
		disconnectSubscription = null;
		isChargedReqSubscription = null;
		chargingRecord           = null;
		chargingTimer            = null;
	}
	
	/** Create Schema for registration **/	
	private void CreateInternalSubscribeItems()
	{			
		LS_SubscriptionItem tmpItem;

		disconnectSubscription = null;
		tmpItem = new LS_SubscriptionItem("TELETEXT");
		//Session Key
		tmpItem.AddSubscribeItem("session" + lsClient.GetSessionKey());
		//Return true false
		tmpItem.AddSubscribeSchema(disconnectSchema);
		disconnectSubscription = new LS_SubscriptionPair(tmpItem, disconnectIdentifier);		

		if (isChargedReqSubscription == null)
		{
			tmpItem = new LS_SubscriptionItem("HOLIDAY");
			tmpItem.AddSubscribeItem("Billing");
			//Return true false
			tmpItem.AddSubscribeSchema(chargeSchema);
			tmpItem.AddSubscribeSchema(chargeSchema2);
			isChargedReqSubscription = new LS_SubscriptionPair(tmpItem, chargeIdentifier);
		}
	}

	/** Internally subscribe report **/
	private void InternalSubscribeItems()
	{		
		CreateInternalSubscribeItems();		
		//Stop if we do not have a section key	
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Internal Susbscription Starts");

		//Under delay mode or BMP Mode, we should not subscribe the payment stuff
		if (IsDelayMode() || IsBMPMode())
		{
			// Subscribe for force disconnect
			if (disconnectSubscription != null)
				lsClient.SubscribeItem(disconnectSubscription.subIdentifier, disconnectSubscription.subsItem);
		}
		else
		{
			List<String> idList;
			List<LS_SubscriptionItem> itemList;
			
			idList = new ArrayList<String>();
			itemList = new ArrayList<LS_SubscriptionItem>();
			
			if (disconnectSubscription != null)
			{
				idList.add(disconnectSubscription.subIdentifier);
				itemList.add(disconnectSubscription.subsItem);
			}
			
			if (isChargedReqSubscription != null)
			{
				idList.add(isChargedReqSubscription.subIdentifier);				
				itemList.add(isChargedReqSubscription.subsItem);
			}		
			
			lsClient.SubscribeItemInBatch(idList, itemList);				
		}
				
		if (debugMode)
			TDStockLog.i(GetIdentity(), "Internal Subscription Done");
	}
	
	/* max of ten minutes in minute */
	private final       int               MAX_CDR_RUNTIME = 600;
	private Object 				   cdrListLock   = new Object();
	private List<IReporterTicker_Report>  cdrListenerList;	
	/** CDR Count Reporter
	 *  **Mean to be used internally */
	public void ReporterTicker_Response(int iSecond)
	{		
		if (cdrDebug && ((iSecond%10) == 0))
			TDStockLog.i(GetIdentity(), "Remain Tick count: " + String.valueOf(iSecond)
						+ ",isActive=" + isActive);

		boolean updateFlag;
		boolean unlimitedUser;
		boolean haveChargingItem;
		
		haveChargingItem = IsAnyChargeItemsOnRequestList();
		updateFlag = false;
		
		//First check we have any payment item and to resume the timer			
		if (haveChargingItem && isActive)
			updateFlag = true;
						
		//For unlimited user, we would let the timer goes on and report the CDR as long as
		//the user continue to subscribe and no need to report to the user
		unlimitedUser = lsClient.IsStreamingUnLimited();				
		// We need to suspend all subscription when the value is at 0
		if (iSecond == 0)
		{
			//Destroy the Timer or this Ticker
			chargingTimer.Dispose();
			chargingTimer = null;
			
			String reportString;
								
			//Send a CDR Report		
			if (chargingRecord != null)
			{
				if (haveChargingItem)
					chargingRecord.SetLastAccessTime();
				
				reportString = FinalizeChargingRecord();
				if (reportString.equals("") == false)
					cdrItemReporter.AddURLToAdvanceStorage(reportString, IMMEDIATE_SEQ.TOP);
				//Please note the startSessionTime and lsTime, is still there
				chargingRecord = null;			
			}		
			
			if (unlimitedUser == false)
			{
				if (haveChargingItem)
				{
					if (cdrDebug)						
						TDStockLog.i(GetIdentity(), "Suspend all clients subscription, because of timeout");					
					LSClient_SuspendAllActivityReqItems();
				}					
			}				
			else if (updateFlag)
			{				
				ChargingTimerCreateIfNeed();
				if (chargingRecord == null)
					chargingRecord = new CDRRecordItem(lsClient.GetLSLastConnectTime());

				chargingRecord.SetStartRunningTime();
				chargingRecord.SetLastAccessTime();
				
				if (chargingTimer != null)
					chargingTimer.Start();		
			}			
		}
		else
		{
			// Check whether our time exceed 10 mins, new rules
			if (chargingRecord != null)
			{		
				long     tmpTickTime;
				boolean  modRecord;
				
				modRecord = false;
				//Round to second is easier for calculation
				tmpTickTime = chargingRecord.CheckUpdateTick()/1000;
				// Wrap up the record
				if (tmpTickTime == MAX_CDR_RUNTIME)
				{
					if (debugMode)
						TDStockLog.i(GetIdentity(), "ReporterTicker_Response=>On Limit CDR Created");
										
					modRecord = chargingRecord.RetrieveLastCheckValue();
				}
				else if (tmpTickTime > MAX_CDR_RUNTIME)
				{
					if (debugMode)
						TDStockLog.i(GetIdentity(), "ReporterTicker_Response=>Over Limit CDR Created");
					
					modRecord = chargingRecord.AdjustToLastCheckValue(MAX_CDR_RUNTIME*1000L);				
				}

				if (debugMode && modRecord)
					TDStockLog.i(GetIdentity(), "**ReporterTicker_Response=>ModRecord: true");
				
				if (modRecord)
				{
					String   reportString;	

					reportString = FinalizeChargingRecord();
					if (reportString.equals("") == false)
						cdrItemReporter.AddURLToAdvanceStorage(reportString, IMMEDIATE_SEQ.TOP);
					//Copy the previous value to the new record, this is not to overcount some missed time
					chargingRecord = new CDRRecordItem(chargingRecord);						
				}
				
			}			
		}		

		//Only unlimited user will get this message
		if (!unlimitedUser && haveChargingItem)
		{
			//Remember this report to whoever subscribe, not limit to 0 second
			//Report to caller if it is a non-delay mode
			if (updateFlag && ((iSecond % 10) == 0))
			{
				Message msg;
				
				msg = new Message();
				msg.what = UPDATE_LS_TIMER_CALLER;
				synchronized(cdrListLock)
				{
					for (IReporterTicker_Report item: cdrListenerList)
					{
						if (item.equals(GetCurrentActivity()) == true)
						{
							msg.obj  = item;
							msg.arg1 =  iSecond;
							localMsgHandler.sendMessage(msg);
						}
					}
				}
			}
		}			
	}
	
	/* Almost promise to have a Timer on hander unless there is an exception */
	private void ChargingTimerCreateIfNeed()
	{		
		if (!isChargeTradingHour && !isChargeTradingHourBookMark)
			return;
		
		//If the timer ticker does not exist, than create the timer
		if (chargingTimer != null)
			return;
		
		try
		{
			int  initialSecond;
			if (lsClient.IsStreamingUnLimited())
				initialSecond = unlimitecdrStartSecond;	
			else
				initialSecond = cdrStartSecond;
			//Set Initial to 60 second
			chargingTimer = new ReporterTicker(this, initialSecond);
			
			if (cdrDebug)
				TDStockLog.i(GetIdentity(), "CreateChargingTimer: Create Reporter Ticker ****" );
		}
		catch (Exception e)
		{
			e.printStackTrace();				
			TDStockLog.e(GetIdentity(), "CreateChargingTimer: " + e.toString());
		}
	}
	
	private void ChargingRecordCreateIfNeed(boolean iSetRunningTime)
	{		
		if (!isChargeTradingHour && !isChargeTradingHourBookMark)
			return;
		
		if (chargingRecord == null)
		{
			chargingRecord = new CDRRecordItem(lsClient.GetLSLastConnectTime());
			if (cdrDebug)
				TDStockLog.i(GetIdentity(), "ChargingRecordCreateIfNeed: Create CDR Record ****");
		}
		
		if (iSetRunningTime)
			chargingRecord.SetStartRunningTime();
	}
	
	
	/************** CDR Relating Stuff, the CDR Timer will not work under ************/
	
	/** When this is called, it will add another 60sec if the timer is not above 60 second **/
	private void ApplyChargingScheme()
	{
		boolean addTimer;
				
		//If we do not have charing signal from server
		if (!isChargeTradingHour && !isChargeTradingHourBookMark)
			return;
		
		if (cdrDebug)
			TDStockLog.i(GetIdentity(), "ApplyCharingScheme Enter");
		
		addTimer = false;
		/* Check whether there are item using the CDR */
		if (isChargeTradingHourBookMark && IsAnyRequestTypeOnList(LS_ItemRequestType.BOOKMARK_CHARGE))
			addTimer = true;
		else if (isChargeTradingHour && IsAnyRequestTypeOnList(LS_ItemRequestType.NORMAL_CHARGE))
			addTimer = true;
		
		//We should add something for our timer
		if (addTimer == false)
			return;
		
		if (cdrDebug)
			TDStockLog.i(GetIdentity(), "ApplyCharingScheme: Checking for Timer to Modify");
		
		//If the timer ticker does not exist, than create the timer
		ChargingTimerCreateIfNeed();

		/*** Add 60 second extra to it ***/
		//Check whether we need to add extra time
		int remainTime;
		
		remainTime = chargingTimer.GetRemainingSecond();
		//It must be a just finish Timer
		if (remainTime < cdrMaxSecond)
		{
			if (chargingTimer != null)
				chargingTimer.IncrementInSecond(cdrIncSecond);
			
			if (cdrDebug)
				TDStockLog.i(GetIdentity(), "ApplyChargingScheme: Purchase extra time");
		}		
	}
	
	private void DischargeChargingScheme()
	{
		boolean cdrTimerStop;
				
		if (!isChargeTradingHour && !isChargeTradingHourBookMark)
			return;
	
		cdrTimerStop = true;	
		/* Check whether there are item using the CDR */
		if (isChargeTradingHourBookMark && IsAnyRequestTypeOnList(LS_ItemRequestType.BOOKMARK_CHARGE))
			cdrTimerStop = false;
		
		if (cdrTimerStop && isChargeTradingHour && IsAnyRequestTypeOnList(LS_ItemRequestType.NORMAL_CHARGE))
			cdrTimerStop = false;
		
		// We should no stopping the cdr timer, but remember the last usage of time
		if (cdrTimerStop == true)
		{
			//The record is used to set as the last access time for the record
			if (chargingRecord != null)
				chargingRecord.SetLastAccessTime();
		}
		
	}
	
	/** Subscribe Internal Items reports **/
	private boolean InternalSubscribeItemsReport( String iIdentifier, int iSchemaIndex, String iSchemaValue, 
							   			UpdateInfo iUpdateInfo)
	{		
		// Check for our internal ID
		// Check for force disconnect
		if (iIdentifier.equals(disconnectIdentifier))
		{
			String newValue;
			boolean  rValue;
			
			newValue = iUpdateInfo.getNewValue(disconnectSchema);
			rValue = false;
			if (newValue.equalsIgnoreCase("true") == true)
				rValue = true;

			if (debugMode)
				TDStockLog.i(GetIdentity(), "** InternalSubscribeItemsReport: Disconnect:" + rValue);
			
			if (rValue != isForceDisconnect)
			{
				isForceDisconnect = rValue;						
				//Close immediately, we could close all things, including our timer, and record
				if (isForceDisconnect)
				{
					String reportString;
					
					//Send a CDR Report
					reportString = FinalizeChargingRecord();
					
					if (reportString.equals("") == false)
						cdrItemReporter.AddURLToAdvanceStorage(reportString, IMMEDIATE_SEQ.TOP);
					//Kill the timer and kill the record
					chargingRecord = null;
					if (chargingTimer != null)
					{
						chargingTimer.Dispose();
						chargingTimer = null;
					}

					LSClient_Disconnect(CONNECTION_STATUS.CLOSE_BY_FORCE);										
				}				
			}
			return true;
		}
		
		//Check for Charging schema
		if (iIdentifier.equals(chargeIdentifier))
		{
			String  newValue;
			boolean charging, chargingBookmark;

			newValue = iUpdateInfo.getNewValue(chargeSchema);
			if (newValue != null)
			{
				if (newValue.equalsIgnoreCase("true") == true)
					charging = true;
				else
					charging = false; 
			}
			else
				charging = isChargeTradingHour;
						
			newValue = iUpdateInfo.getNewValue(chargeSchema2);
			if (newValue != null)
			{
				if (newValue.equalsIgnoreCase("true") == true)
					chargingBookmark = true;
				else
					chargingBookmark = false; 
			}
			else
				chargingBookmark = isChargeTradingHourBookMark;
			
			if (debugMode)
			{
				TDStockLog.i(GetIdentity(), "** InternalSubscribeItemsReport: ChargingHour:" + charging);	
				TDStockLog.i(GetIdentity(), "** InternalSubscribeItemsReport: ChargeBookMark:" + chargingBookmark);
			}				
			
			if (enforceCDRTesting)
				UpdateChargingModule(true, true);	
			else
				UpdateChargingModule(charging, chargingBookmark);
					
			return true;
		}				
		return false;
	}		
		
	
	private void UpdateChargingModule(boolean iNormalSubscribe, boolean iBookmarkSubscribe)
	{
		boolean runCharging;
			
		isChargeTradingHour  	   = iNormalSubscribe;
		isChargeTradingHourBookMark = iBookmarkSubscribe;
		
		runCharging = false;
		if (isChargeTradingHour && IsAnyRequestTypeOnList(LS_ItemRequestType.NORMAL_CHARGE))
			runCharging = true;
		else if (isChargeTradingHourBookMark && IsAnyRequestTypeOnList(LS_ItemRequestType.BOOKMARK_CHARGE))
			runCharging = true;
		
		if (debugMode)
			TDStockLog.i(GetIdentity(), "** UpdateChargingModule: runCharging: " + runCharging
									+ ", isActive=" + isActive);		
		
		//If require to start a recording; Timer + Record
		if (runCharging == true)
		{
			//Ensure our cd should start
			ChargingRecordCreateIfNeed(true);
			if (chargingRecord != null)
				chargingRecord.SetLastAccessTime();
			
			ChargingTimerCreateIfNeed();			
			if (chargingTimer != null)				
				chargingTimer.Start();
			
			return;
		}

		//If previously we have records, or we are running
		String reportString;
			
		reportString = FinalizeChargingRecord();		
		if (reportString.equals("") == false)
			cdrItemReporter.AddURLToAdvanceStorage(reportString, IMMEDIATE_SEQ.TOP);
		
		chargingRecord = null;
		if (chargingTimer != null)
		{
			chargingTimer.Dispose();
			chargingTimer = null;
		}

		/* Resume the subscribed item for user, since it is in a free time */
		if (isActive)
		{			
			LSClient_ResumeAllItems(GetCurrentActivityClass());
		
			Class<? extends Activity> curClass;
		
			curClass = GetCurrentActivityClass();
			if (curClass != null)
			{
				//Report a new time for user
				Message msg;
			
				msg = new Message();
				msg.what = UPDATE_LS_TIMER_CALLER;		
				for (IReporterTicker_Report item: cdrListenerList)
				{
					if (item.equals(curClass) == true)
					{
						msg.obj  = item;
						msg.arg1 =  -1;
						localMsgHandler.sendMessage(msg);
					}			
				}
			}
		}			
	}		
	
	/** Obtain the current remaining second for CDR **/
	public int GetCDRRemainingTime()
	{
		if (chargingTimer == null)
			return 0;
			
		return chargingTimer.GetRemainingSecond();
	}
	
	/** Register to listener to the purchased CDR remaining time
	 * Reports only available on real time mode
	 * RegisterToListenCDRChanges and UnregisterToListenCDRChanges must be work concurrently
	 * @param iCaller instance of IReporterTicker_Report
	 * @return true for successfully register
	 */
	public boolean RegisterToListenCDRChanges(IReporterTicker_Report iCaller)
	{
		if (iCaller == null)
			return false;
		
		synchronized(cdrListLock)
		{			
			if (cdrListenerList.contains(iCaller))
				return true;
			
			cdrListenerList.add(iCaller);			
			//No update to provide this moment
			if (IsDelayMode() || IsBMPMode())
				return true;

			//No Timer provided
			if (chargingTimer == null)
				return true;
			
			Message msg;

			msg = new Message();
			msg.what = UPDATE_LS_TIMER_CALLER;		
			msg.obj = iCaller;	
			msg.arg1 = chargingTimer.GetRemainingSecond();
			localMsgHandler.sendMessage(msg);

			return true;
		}
	}
	
	/** UnRegister to listener to the purchased CDR remaining time
	 * RegisterToListenCDRChanges and UnregisterToListenCDRChanges must be work concurrently	 * 
	 * @param iCaller instance of IReporterTicker_Report
	 * @return true for successfully register
	 */	
	public boolean UnregisterToListenCDRChanges(IReporterTicker_Report iCaller)
	{
		int index;

		if (iCaller == null)
			return false;

		synchronized(cdrListLock)
		{			
			index = cdrListenerList.indexOf(iCaller);
			if (index != -1)			
				cdrListenerList.remove(index);
			return true;
		}
	}
	

	// An internal class that perform the CDR reporting, work with the
	// CDRStorageAccess and the TDStorageReportcdRUsage object.
	// The reason why we are having this on Application_Framework
	// because we want to have the control of the HTTP usage, the padLock
	private class CDRAutoReportQueue implements ITDStockReportCDRUsage_Result
	{
		private final String LOG_TAG = "CDRAutoServerSending";

		private CDRStorageAccess storageObject;
		private TDStockReportCDRUsage reqFeature;
		private boolean               taskRunning;
		private final int inBetweenThreadWait = 35000;
		private final int fastThreadWait      = 6000;
		private final int firstStartWait      = 6000;
		private final int quickStartWait      = 500;
		private TimerTask cdrTimerTask = null;
		private Timer     cdrTimer     = null;
		private boolean   cdrDebugMode = false;
		
		//Call After iContext is created, super.onCreate
		public CDRAutoReportQueue(Context iContext)
		{			
			storageObject = new CDRStorageAccess(iContext);
			//HTTP Sending class for CDR Record
			reqFeature    = new TDStockReportCDRUsage(iContext);

			taskRunning = true;
			//Start our first Task
			startTaskTimer(firstStartWait);
		}

		public void SetDebugMode(boolean iDebugMode)
		{
			cdrDebugMode = iDebugMode;
		}
	
		private void CDRSendMessageAction()
		{
			CDRSingleStorageObject  cdrObj;
			
			if (IsNetworkAvailable() == false)
			{
			    if (cdrDebug)
				    TDStockLog.i(LOG_TAG, "No Network, CDR will send after delay");
				ScheduleForNextSending();
				return;
			}
			
			cdrObj = storageObject.GetNextCDRSendObject();
			//This padLock is use to block all necessary HTTP Connection
			if (cdrObj == null)
			{
				if (cdrDebugMode)
					TDStockLog.i(LOG_TAG, "******* CDRAutoServerSending=>Nothing to send");
				
				ScheduleForNextSending();
				return;
			}						

			if (cdrDebugMode)
				TDStockLog.i(LOG_TAG, "******* CDRAutoServerSending=>Grab a new record");
			
			// If a true is return, the schedule will take care of itself.
			if (reqFeature.SetCDRUpdateParam(cdrObj.idString, cdrObj.urlString, LS_ClientParamMemory.Get_CDRReportPort()) == true)
			{
				reqFeature.SendRequest(this);
			}						
			else
			{
				//Remove it from record and perform the next scan				
				if (cdrDebugMode)
					TDStockLog.i(LOG_TAG, "CDRAutoServerSending=>Thread found invalid url");
				storageObject.RemoveCDRRecord(cdrObj.idString);
				ScheduleForNextSending();
			}					
		}

		private void startTaskTimer(int iStartTimer)
		{  
			if (cdrTimerTask != null)
				return;
			
			cdrTimerTask = new TimerTask()
			{  
				@Override  
				public void run()
				{
					CDRSendMessageAction();
				}
			};

			cdrTimer = new Timer();
			cdrTimer.schedule(cdrTimerTask, iStartTimer);  
		}

		private void stopTaskTimer()
		{  
			if (cdrTimer != null)
			{  
				cdrTimer.cancel();  
				cdrTimer = null;  
			}
			
			if (cdrTimerTask != null)
			{
				cdrTimerTask.cancel();
				cdrTimerTask = null;  
			}
		}

		private void ScheduleForNextSending()
		{			
			//A pause has been set
			if (!taskRunning)
				return;
			
			if (cdrDebugMode)
				TDStockLog.i(LOG_TAG, "ScheduleForNextSending");
			
			stopTaskTimer();
			if (storageObject.HasHighPriorityList())
				startTaskTimer(fastThreadWait);
			else
				startTaskTimer(inBetweenThreadWait);						
		}

		@Override
		public void TDStockReportCDRUsage_Response(TDStockReportCDRUsage_Result iResult)
		{
			//If it is successful, we could remove it from the list
			// We do not care about the result, since the status provides some invalid
			// result.  Hopefully it is not caused by the exception form the sending
			if ((iResult.successfulSend) && !iResult.statusCode.equals(""))
			{
				String statusCode;
				
				statusCode = iResult.statusCode;
				if (statusCode != null)
					storageObject.RemoveCDRRecord(iResult.idString);
			}
			
			//Schedule for the next run if allow
			ScheduleForNextSending();			
		}		
		
		/*** Start the Task to run, can also be called for resume **/
		public void ResumeProcess()
		{
			if (taskRunning)
				return;

			//Consider as a reset
			stopTaskTimer();
			startTaskTimer(quickStartWait);
			taskRunning = true;
			if (cdrDebug)
				TDStockLog.i(LOG_TAG, "Resume Process by creating nextTask");			
		}
		
		/** Pause is to pause the task and wait until a resume or start is called **/
		public void PauseProcess()
		{
			if (!taskRunning)
				return;
			
			stopTaskTimer();
			taskRunning = false;
			if (cdrDebugMode)
				TDStockLog.i(LOG_TAG, "Pause Process, No Task will be updated");
		}
				
		/** Dispose is to destroy the whole task and cannot be re-used
		 * This would cancel all the task and timer and cannot be schedule
		 * anymore **/
		public void Dispose()
		{
			if (cdrDebugMode)
				TDStockLog.i(LOG_TAG, "Object will have no reference anymore");			
			//assume setting this terminated flag would make
			// current URL request as the last URL
			stopTaskTimer();
		}
		
		/** Add new URL item to storage, the storage does
		 *  not check for validity.  For CDR Purpose, there
		 *  should have two %s allowing the API to fill in.
		 *  This has a lower priority than AddURLToAdvanceStorage
		 * @param iURLString
		 */
		public boolean AddURLToStorage(String iURLString)
		{
			storageObject.AddToCDRFileInfo(iURLString);
			//Just wait for the next timeout
			return true;
		}

		/** Add new URL item to storage, the storage does not
		 * check for validity.  For CDR Purpose, there should
		 * have two %2 allowing the API to fill in.  The advance
		 * storage has higher priority than the normal.  It will try to send data immediately
		 * AddURLToStorage
		 * @param iURLString
		 * @param iPos
		 */
		public boolean AddURLToAdvanceStorage(String iURLString, IMMEDIATE_SEQ iPos)
		{			
			storageObject.AddToImmediateList(iURLString, iPos);
			//Only run if the resume has been set
			if (!taskRunning)
				return false;
			
			stopTaskTimer();
			startTaskTimer(quickStartWait);
			return true;
		}
	
		/** Add new URL item to storage, and send immediately as for the next request
		 *  The object is set at the top of all the priority list
		 *  @param iURLString
		 */
		public boolean AddAndSendImmediately(String iURLString)
		{
			//If we are using Immediately, we can not use post or handler to perform this
			//Dump to list top
			storageObject.AddToImmediateList(iURLString, IMMEDIATE_SEQ.TOP);
			
			if (!taskRunning)
				return false;
			
			stopTaskTimer();
									
			CDRSingleStorageObject  cdrObj;
			
			if (IsNetworkAvailable() == false)
			{
			    if (cdrDebugMode)
				    TDStockLog.i(LOG_TAG, "AddAndSendImmediately, CDR will send after delay");
				ScheduleForNextSending();
				return false;
			}
			
			cdrObj = storageObject.GetNextCDRSendObject();			
			// If a true is return, the schedule will take care of itself.
			// If a true is return, the schedule will take care of itself.
			if (reqFeature.SetCDRUpdateParam(cdrObj.idString, cdrObj.urlString, LS_ClientParamMemory.Get_CDRReportPort()) == true)
			{
				reqFeature.SendRequest(this);
				return true;
			}						

			//Remove it from record and perform the next scan				
			if (cdrDebugMode)
				TDStockLog.i(LOG_TAG, "AddAndSendImmediately=>Thread found invalid url");
			storageObject.RemoveCDRRecord(cdrObj.idString);
			ScheduleForNextSending();
			return false;
		}		
	}	
}
